【问题标题】:React Hook useCallback not getting updated state when invokedReact Hook useCallback在调用时没有得到更新的状态
【发布时间】:2020-07-29 01:17:42
【问题描述】:

我正在开发这个应用程序,我双击页面并创建一个新项目。然后我通知 websockets 服务器将这个创建广播给所有其他收听它的用户。在另一个用户端,我必须添加这个新项目,这样每个人都会同步。

我正在使用 react hooks,这是我从头开始使用它的第一个应用程序。我在这个应用程序中遇到了几个类似的问题,但我选择了这个流程来说明基本问题。它必须涵盖大多数问题。

当我调用useCallback 在“其他用户”端添加项目时,items 状态为旧(空)。因此,无论我创建多少次项目,页面上都只会有一个,即我刚刚创建的那个。

这是代码示例:

import React, { useEffect, useState, useCallback } from 'react';
import io from 'socket.io-client';

let socket;

function App() {
  const [items, setItems] = useState({});

  const createItem = useCallback((data) => {
    const id = generateId();
    const newItem = { id, ...data };
    const newItems = { ...items, [id]: newItem };

    setItems(newItems)

    // When I create a new item, I notify the server to broadcast it
    socket.emit('itemCreated', newItem)
  }, [items, setItems])

  const doCreateItem = useCallback((item) => {
    // When someone else creates an item, it should be invoked be invoked
    const newItems = { ...items, [item.id]: item };
    setItems(newItems)
  }, [items]);

  useEffect(() => {
    // Connects to the server
    socket = io('ws://localhost:8090', {
      transports: ['websocket'],
      path: '/ws'
    });

    // Listens to event
    socket.on('createitem', (data) => {
      doCreateItem(data)
    });

    // I cannot add doCreateItem here, otherwise it will call this effect everytime doCreateItem is executed
  }, []);

  return (
    <div
      onDoubleClick={
        (e) => {
          createItem({});
        }
      }
    >

      {
        Object.values(items).map(item => {
          return (
            <div>{item.id}</div>
          )
        })
      }

    </div>
  );
}

export default App;

doCreateItem 具有项目依赖项。另一方面,useEffect 依赖列表不包含doCreateItem,主要是因为每当我从服务器收到新项目时,它都会重新调用useEffect 回调。也因为我希望这个效果只执行一次(在组件安装上)。我尝试将useEffect 一分为二,一个用于连接,另一个用于侦听器,但同样的情况发生了。

我实际上遇到了几个像这样的问题。回调执行的次数超出了我的预期,或者回调中的状态未更新。

有人能说明我们应该如何解决这个问题吗?

提前致谢!

编辑

按照 @Ross 的建议,我更改了 setItems 调用以接收回调并返回新状态。这有助于 setItems 依赖。但我仍然无法在doCreateItem 函数中读取items。它总是空的。

我注意到的一件有趣的事情是,如果我停止使用 useCallback 并在每次渲染时重新创建函数,它就没有该函数的更新状态,但它确实有函数之外的状态:

function App() {
  const [items, setItems] = useState({});
 
  console.log('Rendered again and knows the state >', items)
  const doCreatePostIt = (data) => {
    console.log('Empty >', items)
    // ...
  };
  // ...
}

【问题讨论】:

    标签: javascript reactjs socket.io react-hooks react-lifecycle-hooks


    【解决方案1】:

    当下一个状态依赖于当前状态时,使用useState 的functional update 形式消除了依赖数组中的一个依赖,解决了“陈旧状态”问题。

    • 从useState 钩子返回的setX 函数是稳定的,不会在渲染之间改变(参见useState doc);从依赖数组中省略是安全的
    const [items, setItems] = useState({});
    
    const createItem = useCallback((data) => {
      const id = generateId();
      const newItem = { id, ...data };
    
      setItems((currItems) => ({
        ...currItems,
        [id]: newItem,
      }))
    
      socket.emit('itemCreated', newItem)
    }, [])
    
    useEffect(() => {
      function doCreateItem(item) {
        setItems((currItems) => ({
          ...currItems,
          [item.id]: item,
        }))
      }
    
      socket = io('ws://localhost:8090', {
        transports: ['websocket'],
        path: '/ws'
      });
    
      socket.on('createitem', doCreateItem);
    
      // Return a clean up function so the component will stop listening to the
      // socket when the component is unmounted.
      return () => {
        socket.off('createItem', doCreateItem)
      }
    }, []);
    

    【讨论】:

    • 从测试中排除 doCreateItem 并仅更改 createItem,会发生同样的错误。每次调用该函数时,items 对象仍然为空。我还更改了 setItems 以使用回调,就像您所做的那样并且没有工作。 onDoubleClick 事件、状态和useCallback 之间有问题
    • 实际代码比我贴在这里的部分要长很多。评论其他所有内容并只留下createItem 使其工作。可能还有其他东西在清空对象。一旦我意识到什么,我会在这里发帖
    • 这有帮助,但我仍然对 items 对象有疑问。我仍然无法在doCreateItem 中阅读items。我发现在doCreateItem 中提供它的唯一方法是将它添加到useEffect。这种情况下的问题是,每次更新 items 时,我都会在套接字中附加新的侦听器。不打算执行useEffect 回调。
    • @VictorFerreira 我更新了我的答案。在useEffect 中声明doCreateItem,这样它就不需要依赖了。确保在卸载组件时清理套接字。
    猜你喜欢
    • 1970-01-01
    • 2020-03-03
    • 1970-01-01
    • 1970-01-01
    • 2020-04-11
    • 2020-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多