2017-08-13 60 views
0

好了,所以我已经得到了很远的创建做出反应组件的ChartJS,但测试时我得到以下错误:类型错误:无法读取空的特性“长度”的反应成分

FAIL lib\chart\chart.test.tsx 
    ● renders without crashing 

    TypeError: Cannot read property 'length' of null 

     at Object.acquireContext (node_modules/chart.js/src/platforms/platform.dom.js:189:19) 
     at Chart.construct (node_modules/chart.js/src/core/core.controller.js:72:27) 
     at new Chart (node_modules/chart.js/src/core/core.js:7:8) 
     at Chart.Object.<anonymous>.Chart.renderChart (lib/chart/chart.tsx:233:26) 
     at Chart.Object.<anonymous>.Chart.componentDidMount (lib/chart/chart.tsx:42:10) 
     at node_modules/react-dom/lib/ReactCompositeComponent.js:264:25 
     at measureLifeCyclePerf (node_modules/react-dom/lib/ReactCompositeComponent.js:75:12) 
     at node_modules/react-dom/lib/ReactCompositeComponent.js:263:11 
     at CallbackQueue.notifyAll (node_modules/react-dom/lib/CallbackQueue.js:76:22) 
     at ReactReconcileTransaction.close (node_modules/react-dom/lib/ReactReconcileTransaction.js:80:26) 
     at ReactReconcileTransaction.closeAll (node_modules/react-dom/lib/Transaction.js:209:25) 
     at ReactReconcileTransaction.perform (node_modules/react-dom/lib/Transaction.js:156:16) 
     at batchedMountComponentIntoNode (node_modules/react-dom/lib/ReactMount.js:126:15) 
     at ReactDefaultBatchingStrategyTransaction.perform (node_modules/react-dom/lib/Transaction.js:143:20) 
     at Object.batchedUpdates (node_modules/react-dom/lib/ReactDefaultBatchingStrategy.js:62:26) 
     at Object.batchedUpdates (node_modules/react-dom/lib/ReactUpdates.js:97:27) 
     at Object._renderNewRootComponent (node_modules/react-dom/lib/ReactMount.js:319:18) 
     at Object._renderSubtreeIntoContainer (node_modules/react-dom/lib/ReactMount.js:401:32) 
     at Object.render (node_modules/react-dom/lib/ReactMount.js:422:23) 
     at Object.<anonymous> (lib/chart/chart.test.tsx:7:12) 
      at Promise (<anonymous>) 
     at Promise.resolve.then.el (node_modules/p-map/index.js:42:16) 
      at <anonymous> 
     at process._tickCallback (internal/process/next_tick.js:169:7) 

    × renders without crashing (275ms) 

Test Suites: 1 failed, 1 total 
Tests:  1 failed, 1 total 
Snapshots: 0 total 
Time:  1.314s, estimated 3s 
Ran all test suites related to changed files. 

然而,我花了很长时间来查看代码,一直没有弄清楚为什么它拒绝正常工作。在创建新图表实例时,该错误从renderChart()函数开始。我的第一个猜测是,由于某些原因,尽管被它的id调用,它仍然没有注册canvas元素。但是,当renderChart的内容被移入render()函数时,它仍会给出相同的错误。这里的代码被测试:

import * as React from 'react' 
import * as ClassNames from 'classnames' 
import * as ChartJS from 'chart.js' 
const IsEqual = require('lodash.isequal') 
const Find = require('lodash.find') 
const subChart = require('chart.js') 

interface IChartProps { 
    /** The user-defined classes */ 
    readonly className?: string 
    readonly width?: number 
    readonly height?: number 
    readonly reRender?: boolean 

    readonly type: ChartJS.ChartType 
    readonly data: ChartJS.ChartData 
    readonly options: ChartJS.ChartOptions 
    readonly getDatasetAtEvent?: Function 
    readonly getElementAtEvent?: Function 
    readonly getElementsAtEvent?: Function 
    readonly onElementsClick?: Function 
    readonly datasetKeyProvider?: Function 
} 

interface IChartState { 
    /** Add your states here */ 
} 

export class Chart extends React.Component<IChartProps, IChartState> { 
    // tslint:disable-next-line 
    private chartInstance: any 
    private shadowData: {} 
    constructor(props: IChartProps) { 
    super(props) 
    } 

    public componentWillMount() { 
    // this.chartInstance = undefined 
    } 

    public componentDidMount() { 
    this.renderChart() 
    } 

    // public componentWillReceiveProps(nextProps: IChartProps) {} 

    public shouldComponentUpdate(nextProps: IChartProps, nextState: IChartState) { 
    const props = this.props 
    if (nextProps.reRender === true) { 
     return true 
    } 

    if (props.height !== nextProps.height || props.width !== nextProps.width) { 
     return true 
    } 

    if (props.type !== nextProps.type) { 
     return true 
    } 

    if (!IsEqual(props.options, nextProps.options)) { 
     return true 
    } 

    const nextData = this.transformDataProp(nextProps) 

    if (!IsEqual(this.shadowData, nextData)) { 
     return true 
    } 

    return false 
    } 

    // public componentWillUpdate(nextProps: IChartProps, nextState: IChartState) {} 

    public componentDidUpdate(prevProps: IChartProps, prevState: IChartState) { 
    if (this.props.reRender) { 
     this.chartInstance.destroy() 
     this.renderChart() 
     return 
    } 
    this.updateChart() 
    } 

    public transformDataProp(props: IChartProps) { 
    const data = props.data 
    if (typeof data === 'function') { 
     const node = document.getElementById('bar-chart') as HTMLCanvasElement 
     return data(node) 
    } else { 
     return data 
    } 
    } 

    public memoizeDataProps(props?: IChartProps) { 
    if (!this.props.data) { 
     return 
    } 
    const data = this.transformDataProp(this.props) 

    this.shadowData = { 
     ...data, 
     datasets: 
     data.datasets && 
     data.datasets.map((set: string[]) => { 
      return { ...set } 
     }) 
    } 
    return data 
    } 

    public updateChart() { 
    const options = this.props.options 

    const data = this.memoizeDataProps(this.props) 

    if (!this.chartInstance) { 
     return 
    } 

    if (options) { 
     this.chartInstance.options = subChart.helpers.configMerge(
     this.chartInstance.options, 
     options 
    ) 
    } 

    let currentDatasets = 
     (this.chartInstance.config.data && 
     this.chartInstance.config.data.datasets) || 
     [] 
    const nextDatasets = data.datasets || [] 

    const currentDatasetKeys = currentDatasets.map(
     this.props.datasetKeyProvider 
    ) 
    const nextDatasetKeys = nextDatasets.map(this.props.datasetKeyProvider) 
    const newDatasets = nextDatasets.filter(
     (d: object) => 
     currentDatasetKeys.indexOf(this.props.datasetKeyProvider(d)) === -1 
    ) 

    for (let idx = currentDatasets.length - 1; idx >= 0; idx -= 1) { 
     const currentDatasetKey = this.props.datasetKeyProvider(
     currentDatasets[idx] 
    ) 
     if (nextDatasetKeys.indexOf(currentDatasetKey) === -1) { 
     // deleted series 
     currentDatasets.splice(idx, 1) 
     } else { 
     const retainedDataset = Find(
      nextDatasets, 
      (d: object) => this.props.datasetKeyProvider(d) === currentDatasetKey 
     ) 
     if (retainedDataset) { 
      // update it in place if it is a retained dataset 
      currentDatasets[idx].data.splice(retainedDataset.data.length) 
      retainedDataset.data.forEach((point: number, pid: number) => { 
      currentDatasets[idx].data[pid] = retainedDataset.data[pid] 
      }) 
      // const { data, ...otherProps } = retainedDataset 
      currentDatasets[idx] = { 
      data: currentDatasets[idx].data, 
      ...currentDatasets[idx], 
      ...retainedDataset.otherProps 
      } 
     } 
     } 
    } 
    // finally add any new series 
    newDatasets.forEach((d: object) => currentDatasets.push(d)) 
    const { datasets, ...rest } = data 

    this.chartInstance.config.data = { 
     ...this.chartInstance.config.data, 
     ...rest 
    } 

    this.chartInstance.update() 
    } 

    public componentWillUnmount() { 
    this.chartInstance.destroy() 
    } 

    public onClickEvent = (event: React.MouseEvent<HTMLCanvasElement>) => { 
    // this.props.getDatasetAtEvent && 
    this.props.getDatasetAtEvent(
     this.chartInstance.getDatasetAtEvent(event), 
     event 
    ) 

    // this.props.getElementAtEvent && 
    this.props.getElementAtEvent(
     this.chartInstance.getElementAtEvent(event), 
     event 
    ) 

    // this.props.getElementsAtEvent && 
    this.props.getElementsAtEvent(
     this.chartInstance.getElementsAtEvent(event), 
     event 
    ) 

    // this.props.onElementsClick && 
    this.props.onElementsClick(
     this.chartInstance.getElementsAtEvent(event), 
     event 
    ) 
    } 

    public render() { 
    const className = ClassNames('chart', this.props.className) 

    // bar.update() 
    return (
     <div className={className}> 
     <canvas 
      id="chart-instance" 
      width={this.props.width ? this.props.width : '400'} 
      height={this.props.height ? this.props.height : '400'} 
      onClick={this.onClickEvent} 
     /> 
     </div> 
    ) 
    } 

    public renderChart() { 
    const { options, type, data } = this.props 
    const node = document.getElementById('chart-instance') as HTMLCanvasElement 
    // const data = this.memoizeDataProps() 

    this.chartInstance = new ChartJS(node, { 
     type, 
     data, 
     options 
    }) 
    } 
} 

有人可以帮我找出为什么这将无法正常工作?

+0

错误是告诉你的问题是什么。无论你打电话“长”是否存在。你能发布失败的测试吗?即使该测试失败,组件是否呈现? – archae0pteryx

+0

既然你还没有发布自己的测试,我认为“长度”是某个元素的属性。试试像这样: 'const wrapper = mount(); ('ElementYourWantLengthOf')。props()。length' 您可能还会对'find()'返回的节点数组调用'length'。然后,显然,find()没有发现任何可能是组件未正确安装的结果。 反正,发布你的测试代码,然后我们会看到。 –

回答

0

这可能是因为这样的:

currentDatasets[idx].data.splice(retainedDataset.data.length)

你应该有retainedDataset.data还需要检查:

if (retainedDataset && retainedDataset.data) { ... }

相关问题