React子组件无法感知来自useEffect内部异步函数的状态变化
P粉609866533
P粉609866533 2023-09-02 12:55:44
0
1
524
<p>我试图传递一个在异步函数内部生成的状态,该函数本身位于 useState 内部。我了解到 useRef 可能是解决此问题的最佳方法,因为它可以引用可变状态,并且在这个过程中了解了 React-useStateRef,它最终解决了我的主组件内状态永远不会更新的另一个问题(会不断得到“渲染太多”错误)。因此它本质上充当 useRef 和 useState 的合二为一。</p> <p>但是,虽然我的状态最终更新了,但它仍然没有传递到我的 Canvas 组件。我正在尝试根据从数据集中获得的温度来更新画布的 BG 图形。</p> <pre class="brush:php;toolbar:false;">import { useEffect } from 'react'; import useState from 'react-usestateref'; import { getServerData } from './serviceData'; import "./App.css" import Canvas from './components/Canvas'; function App() { const [dataset, setDataset] = useState(null); const [units, setUnits] = useState('metric'); // canvas background graphic stuff var [currentBG, setCurrentBG, currentBGref] = useState(null); useEffect(() => { const fetchServerData = async () => { const data = await getServerData(city, units); setDataset(data); function updateBG() { const threshold = units === "metric" ? 20 : 60; if (data.temp <= threshold) { setCurrentBG('snow'); } else { setCurrentBG('sunny'); } } updateBG(); } fetchServerData(); console.log(currentBG) }, [city, units, currentBGref.current, currentFGref.current]) const isCelsius = currentUnit === "C"; button.innerText = isCelsius ? "°F" : "°C"; setUnits(isCelsius ? "metric" : "imperial"); }; return ( <div className="app"> { dataset && ( <Canvas width={640} height={480} currentBG={currentBGref.current}></Canvas> )} </div> ); } export default App;</pre> <p>我只能传递初始值,并且它永远不会更新超过该值,尽管 useEffect 内部的 console.log 显示它肯定正在更新。那么为什么它没有传递给我的组件呢?</p>
P粉609866533
P粉609866533

全部回复(1)
P粉501007768

useStateRef似乎是一种反模式。您决定新状态是什么,所以如果您需要另一个对它的引用,您总是可以自己创建一个。我建议尽量减少画布上的属性,以防止不必要的重新渲染。

function App({ width, height }) {
  const canvas = React.useRef()
  const [color, setColor] = React.useState("white")

  // when color changes..
  React.useEffect(() => {
    if (canvas.current) {
      const context = canvas.current.getContext('2d');
      context.fillStyle = color
      context.fillRect(0, 0, width, height)
    }
  }, [color, width, height])

  return <div>
    <canvas ref={canvas} width={width} height={height} />
    <button onClick={_ => setColor("blue")} children="blue" />
    <button onClick={_ => setColor("green")} children="green" />
    <button onClick={_ => setColor("red")} children="red" />
  </div>
}

ReactDOM.createRoot(document.querySelector("#app")).render(<App width={200} height={150} />)
canvas { display: block; border: 1px solid black; }
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>

或者也许没有理由将颜色存储为状态。您可以自己创建setColor函数并将其附加为事件监听器 -

function App({ width, height }) {
  const canvas = React.useRef()
  const setColor = color => event => {
    if (canvas.current) {
      const context = canvas.current.getContext('2d');
      context.fillStyle = color
      context.fillRect(0, 0, width, height)
    }
  }
  return <div>
    <canvas ref={canvas} width={width} height={height} />
    <button onClick={setColor("blue")} children="blue" />
    <button onClick={setColor("green")} children="green" />
    <button onClick={setColor("red")} children="red" />
  </div>
}

ReactDOM.createRoot(document.querySelector("#app")).render(<App width={200} height={150} />)
canvas { display: block; border: 1px solid black; }
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>

我还建议您查看SVG。我发现SVG的API与React模式更加契合,比Canvas API更好。每个的功能不同,但如果SVG适用于您的需求,那么值得考虑。

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板