最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Updating a component including a canvas with React - Stack Overflow

programmeradmin0浏览0评论

So I'm trying to modify the state of a ponent which contains a canvas element. The canvas itself should not update since the state affected does not affect the rendering of the 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});
    }

    ponentDidMount() {
        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>
        );
    }
}

Yet an error shows up when I trigger the event onClick of the canvas :

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

So I'm trying to modify the state of a ponent which contains a canvas element. The canvas itself should not update since the state affected does not affect the rendering of the 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});
    }

    ponentDidMount() {
        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>
        );
    }
}

Yet an error shows up when I trigger the event onClick of the canvas :

Uncaught TypeError: Cannot read property 'getContext' of null
at ref (App.js:52)
Share Improve this question asked Dec 10, 2017 at 12:37 Sara DoeSara Doe 1451 gold badge2 silver badges9 bronze badges 0
Add a ment  | 

3 Answers 3

Reset to default 1

React ponent will re-render itself on any of its state property change. If you want control on this behavior, consider overriding shouldComponentUpdate method. If you return false from this method for any state condition, your ponent will not re-render for that condition.

Now, regarding the error, you should move the arrow function definition of ref into a function reference.

The reason is, arrow function will always be passed as new instance while re-rendering, while the function reference will be passed only once during first time render.

Read more from here to know about this in more detail.

Your implementation should be as follows:

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 });

  }

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

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

Move the Canvas to it's own class based ponent then use shouldComponentUpdate as mentioned above, if you're rendering Canvas directly in the render method of this ponent it will re-render every time something is changed, but since you need the state to be updated you can not specify which elements need to be re-rendered unless these elements are own ponents and have should ponent update. You can then pass callback functions to get ref and onClick methods.

React docs Refs Caveats explain why you get the getContext() of null.

If the ref callback is defined as an inline function, it will get called twice during updates, first with null and then again with the DOM element. This is because a new instance of the function is created with each render, so React needs to clear the old ref and set up the new one. You can avoid this by defining the ref callback as a bound method on the class, but note that it shouldn’t matter in most cases. In your case it matters since you're calling ctx.getContext("2d").

For getting rid of unnecessary renders of canvas, as already mentioned from other answers, encapsulate isPlaying in a React.PureComponent and municate changes through an onChange prop.

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");
  }

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

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

评论列表(0)

  1. 暂无评论