使用React更新包含画布的组件

时间:2017-12-10 12:37:03

标签: javascript reactjs html5-canvas

所以我试图修改包含canvas元素的组件的状态。画布本身不应该更新,因为受影响的状态不会影响画布的渲染吗?

import React from 'react';

export default class App extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            isPlaying: false
        }
    }

    handleClick() {
        this.setState({isPlaying: true});
    }

    componentDidMount() {
        this.ctx.fillRect(50,50, 100, 100);
    }

    render() {
        return(
            <div id='container'>
                <canvas width={900} height={500}
                    ref={r => this.ctx = r.getContext('2d')}
                    onClick={() => this.handleClick()} />
            </div>
        );
    }
}

当我触发画布onClick时,会出现错误:

Uncaught TypeError: Cannot read property 'getContext' of null
at ref (App.js:52)

3 个答案:

答案 0 :(得分:0)

React组件将在其任何状态属性更改时重新呈现。如果要控制此行为,请考虑覆盖shouldComponentUpdate方法。如果您针对任何false条件从此方法返回state,则您的组件将不会针对该条件重新渲染。

现在,关于错误,您应该将ref的箭头函数定义移动到函数引用中。

原因是,箭头函数将在重新渲染时始终作为新实例传递,而函数引用将在第一次渲染时仅传递一次。

here了解详情,了解更多详情。

您的实施应如下:

import React from "react";

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isPlaying: false
    };
    this.setContext = this.setContext.bind(this);
  }

  setContext(r) {
    console.log(r);
    this.ctx = r.getContext("2d");
  }

  handleClick(e) {
    this.setState({ isPlaying: true });

  }

  componentDidMount() {
    this.ctx.fillRect(50, 50, 100, 100);
  }

  render() {
    return (
      <div id="container">
        <canvas
          width={900}
          height={500}
          ref={this.setContext}
          onClick={() => this.handleClick()} />
        />
      </div>
    );
  }
}

答案 1 :(得分:0)

React docs docs解释了为什么会得到getContext() of null

如果将ref回调定义为内联函数,则在更新期间将调用它两次,首先调用null ,然后再调用DOM元素。这是因为每个渲染都会创建一个新的函数实例,因此React需要清除旧的ref并设置新的ref。您可以通过将ref回调定义为类的绑定方法来避免这种情况,但请注意,在大多数情况下它应该无关紧要。在您的情况下,这很重要,因为您正在调用ctx.getContext("2d")

为了摆脱canvas的不必要的渲染,正如其他答案中已经提到的那样,将isPlaying封装在React.PureComponent中,并通过onChange道具传达变化。

import * as React from 'react';

export class Canvas extends React.PureComponent {
  state = {
    isPlaying: false
  }

  handleClick(e) {
    const isPlaying = !this.state.isPlaying;
    this.setState({isPlaying});
    this.props.onChange && this.props.onChange(isPlaying)
  }

  setRef = (ctx) => {
    this.ctx = ctx.getContext("2d");
  }

  componentDidMount() {
    this.ctx.fillRect(50, 50, 100, 100);
  }

  render() {
    return (
     <canvas
        width={900}
        height={500}
        ref={this.setRef}
        onClick={() => this.handleClick()}
      />
    );
  }
}

答案 2 :(得分:0)

将Canvas移动到它自己的基于类的组件,然后使用 shouldComponentUpdate ,如上所述,如果您直接在此组件的render方法中渲染Canvas,它将重新每次更改某些内容时都会渲染,但由于您需要更新状态,因此无法指定需要重新呈现哪些元素,除非这些元素是自己的组件并且应该更新组件。然后,您可以传递回调函数以获取ref和onClick方法。