[英]React Hook useCallback not getting updated state when invoked
I am developing this application where I double click the page and create a new item.我正在开发这个应用程序,我双击页面并创建一个新项目。 Then I notify a websockets server to broadcast this creation to all other users listening to it.
然后我通知 websockets 服务器将这个创建广播给所有其他收听它的用户。 On the other user end I must add this new item, so everybody gets synchronized.
在另一个用户端,我必须添加这个新项目,所以每个人都会同步。
I am using react hooks and this is my first app from the scratch using it.我正在使用反应钩子,这是我从头开始使用它的第一个应用程序。 I am having several problems like this in this application, but I selected this flow to illustrate the basic problem.
我在这个应用程序中遇到了几个类似的问题,但我选择了这个流程来说明基本问题。 It must cover most issues.
它必须涵盖大多数问题。
When I call the useCallback
to add the item on the "other user" side, the items
state is old (empty).当我调用
useCallback
在“其他用户”端添加项目时, items
state 是旧的(空)。 So no matter how many times I create items, I will only have one on the page, the one I just created.因此,无论我创建多少次项目,页面上都只会有一个,即我刚刚创建的那个。
This is a sample of the code:这是代码示例:
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;
The doCreateItem
has the items dependency. doCreateItem
具有项目依赖项。 One the other hand, the useEffect
dependency list doesn't contain doCreateItem
mostly because it will reinvoke the useEffect
callback everytime I receive a new item from the server.另一方面,
useEffect
依赖列表不包含doCreateItem
主要是因为它会在我每次从服务器收到新项目时重新调用useEffect
回调。 And also because I want this effect to be executed only once (on component mounting).也因为我希望这个效果只执行一次(在组件安装上)。 I tried splitting the
useEffect
in two, one for the connection and another for the listeners, but the same happens.我尝试将
useEffect
两部分,一个用于连接,另一个用于侦听器,但同样的情况发生了。
I actually had several problems like this one.我实际上有几个像这样的问题。 A callback is executed more than I was expecting or the state is not updated on the callback.
回调执行的次数超出了我的预期,或者 state 未在回调中更新。
Could someone clarify how we should get around this problem?有人可以澄清我们应该如何解决这个问题吗?
Thanks in advance!提前致谢!
Edit编辑
As suggested by @Ross , I changed the setItems
calls to receive a callback and return the new state.正如@Ross所建议的,我更改了
setItems
调用以接收回调并返回新的 state。 That helped with the setItems
dependency.这有助于
setItems
依赖。 But I still can't read items
in the doCreateItem
function.但我仍然无法读取
doCreateItem
function 中的items
。 It is always empty.它总是空的。
Something funny that I noticed, is that if I stop using the useCallback
and recreate the functions on each render, it doesn't have the updated state on that function, but it does have outside the function:我注意到有趣的是,如果我停止使用
useCallback
并在每个渲染上重新创建函数,它在 function 上没有更新的 state,但它确实在 ZC1C425268E68385D14ZA 之外:
function App() {
const [items, setItems] = useState({});
console.log('Rendered again and knows the state >', items)
const doCreatePostIt = (data) => {
console.log('Empty >', items)
// ...
};
// ...
}
When the next state depends on the current one, using the functional update form of useState
eliminates a dependency in the dependency array and solves the "stale state" problem.当下一个 state 依赖于当前的时,使用
useState
的函数更新形式消除了依赖数组中的一个依赖,解决了“stale state”问题。
setX
functions returned from the useState
hook is stable and does not change between renders (see useState
doc );useState
挂钩返回的setX
函数是稳定的,不会在渲染之间改变(参见useState
文档); it is safe to omit from dependency arraysconst [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)
}
}, []);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.