2017-08-13 61 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) { ... }

相關問題