所以我试图修改包含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)
答案 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方法。