【问题标题】:React setState hook async updating issueReact setState hook异步更新问题
【发布时间】:2020-09-09 17:56:40
【问题描述】:

我正在尝试在数据发生更改时更新屏幕上的一些内容。我正在使用带有钩子的 React 函数组件。当父元素中的数据更新时,我通过 props 将其发送到PlayerControls:

export const PlayerControls = (props) => {
....

  const [heatmaps, setHeatMaps] = React.useState("");
  const [data, setData] = React.useState(null);

  useEffect(fetchData, [props.videoData]);
  useEffect(updateHeatmaps, [data]);

  function fetchData () {
     d3.csv(require(`./data/${props.videoData}`)).then((d) => setData(d));
  }

  function updateHeatmaps(){
        if (data && data.length) {
            const temp = data.map((item, index) => {
                return (
                    <Heatmap size={[400, 50]} data={item} key={index}/>
                )
            });
        setHeatMaps(temp);
        }
   }

   return (
       <div>
         {heatmaps}
       </div>
   );

}

问题是,虽然这对初始数据有效,但它不会更新屏幕上的heatmaps。现在,我熟悉setState 钩子是异步的,并且有一些解决方案(例如使用useEffect 钩子更新状态并将状态添加为依赖项),例如this StackOverflow question。问题是我正在使用另一个函数更新状态,我不能在那里或在返回函数中使用useEffect。我真的很感激任何解决方案,这个问题已经拖了我一段时间了,让我想知道这些钩子和函数组件是否是所有用例的解决方案。

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案1】:

    不是渲染heatmaps字符串,而是在渲染时返回映射的数据(如果存在):

    export const PlayerControls = (props) => {
        const [data, setData] = React.useState(null);
        useEffect(fetchData, [props.videoData]);
    
        function fetchData() {
            d3.csv(require(`./data/${props.videoData}`)).then((setData);
        }
    
        return (
            <div>
                {data?.map((item, index) => (
                    <Heatmap size={[400, 50]} data={item} key={index} />
                ))}
            </div>
        );
    }
    

    示例 sn-p:

    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
    const getData = () => Promise.resolve(['foo', 'bar']);
    const PlayerControls = (props) => {
        const [data, setData] = React.useState(null);
        React.useEffect(fetchData, [props.videoData]);
    
        function fetchData() {
            getData().then(setData);
        }
    
        return (
            <div>
                {data && data.map((item, index) => (
                    <Heatmap size={[400, 50]} data={item} key={index} />
                ))}
            </div>
        );
    }
    const Heatmap = ({ data }) => <div>{data}</div>;
    ReactDOM.render(<PlayerControls />, document.querySelector('.react'));
    </script>
    <div class="react"></div>

    【讨论】:

    • 我对您的回答感到非常兴奋,但它不起作用。我的猜测是您的解决方案没有检测到数据已更改?
    • 哦,updateHeatmaps 已经不用了,所以可以完全删除。之前可能会抛出 ReferenceError
    • 是的,我明白了,但它仍然不起作用,我的猜测是,不知何故,没有什么会触发数据的变化。 (更具体地说,当我说它不起作用时,我的意思是它没有显示您的解决方案的变化,类似于我的解决方案)——尽管您的解决方案仍然适用于初始数据。 :(
    • 任何控制台错误?你确定.then 正在运行吗? (与往常一样,最好在 Promises 中添加 .catch 以检查它们是否拒绝)setData 是否使用数组调用?
    • 我用一个虚拟的 Promise 做了一个示例 sn-p。它看起来可以按预期工作 - 按“运行代码 sn-p”以查看正在呈现的 foobar。如果它对您不起作用,听起来问题不在问题中发布的代码范围内
    【解决方案2】:

    因此,正如上面 cmets 中提到的@CertainPerformance,问题不在于useState 挂钩,而在于updateHeatmap 函数内部。显然,当使用.map 函数时,您的键应该是全局唯一的。我使用index作为key,由于更新后这个索引没有变化,React没有检测到变化,也没有更新heatmaps状态。

    变化

    const temp = data.map((item, index) => {
         return (
                 <Heatmap size={[400, 50]} data={item} key={index}/>
                )
         });
    

    const temp = data.map((item, index) => {
         let key = props.videoData + '.' + index;
         return (
                 <Heatmap size={[400, 50]} data={item} key={key}/>
                )
         });
    

    解决了我的问题。我希望这可以帮助其他面临类似问题的人!

    【讨论】:

      猜你喜欢
      • 2020-03-03
      • 1970-01-01
      • 1970-01-01
      • 2020-01-31
      • 2021-08-02
      • 1970-01-01
      • 1970-01-01
      • 2019-02-13
      • 2020-11-04
      相关资源
      最近更新 更多