开发者问题收集

React canvas TypeError:重新渲染后无法读取 null 的属性“getContext”

2021-05-25
5458

我有一个画布,它在引用中使用 canvas.getContext( "2d" )。 我的问题是,当页面重新渲染时,由于状态发生变化,我收到错误,我不知道如何修复它。

这是一个出现错误的最小代码示例:

import React, { useState } from "react";
import { render } from "react-dom";

const Test: React.FC<{}> = () => {
    const [ counter, setCounter ] = useState<number>( 0 );

    return (
        <div>
            <button onClick={ () => { setCounter( counter + 1 ) } }>Test</button>
        <canvas ref={ canvas => {
            let context = canvas.getContext( "2d" );
        } }/>
        </div>
    );
}

render( <Test />, document.getElementById( "root" ) );

这是错误:

canvas_test.component.tsx:11 Uncaught TypeError: Cannot read property 'getContext' of null
    at ref (canvas_test.component.tsx:11)
    at commitDetachRef (react-dom.development.js:20893)
    at commitMutationEffects (react-dom.development.js:23348)
    at HTMLUnknownElement.callCallback (react-dom.development.js:3945)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:3994)
    at invokeGuardedCallback (react-dom.development.js:4056)
    at commitRootImpl (react-dom.development.js:23121)
    at unstable_runWithPriority (scheduler.development.js:468)
    at runWithPriority$1 (react-dom.development.js:11276)
    at commitRoot (react-dom.development.js:22990)

为什么会出现这个错误,我该如何修复它?

2个回答

您可以在 useEffect 中初始化画布引用,这样可以在组件挂载或组件更新后立即调用函数。(在画布元素在 dom 中可用之后)

import React, {useState, useEffect, useRef} from "react";

export default function App() {
  const [counter, setCounter] = useState(0);
  const canvasRef = useRef(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    const context = canvas.getContext("2d");
  }, []);

  return (
    <div>
      <button
        onClick={() => {
          setCounter(counter + 1);
        }}
      >
        Test
      </button>
      <canvas ref={canvasRef} />
    </div>
  );
}
SebMaz
2021-05-25

根据 回调引用

React will call the ref callback with the DOM element when the component mounts, and call it with null when it unmounts .

并且根据 回调引用的注意事项

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.

我相信您看到的是后者:每次调用 setCounter 都会触发更新,如上所述,它会使用 null 调用您的回调。

解决方案

在您的回调中,处理 null 条件。我建议将回调提取到其自己的函数中。例如:

const Test: React.FC<{}> = () => {
    const [ counter, setCounter ] = useState<number>( 0 );

    const refHandler = (canvas) => {
        if (!canvas) return;

        let context = canvas.getContext( "2d" );
    }

    return (
        <div>
            <button onClick={ () => { setCounter( counter + 1 ) } }>Test</button>
        <canvas ref={refHandler} />
        </div>
    );
}
rsmeral
2021-05-25