useState-value在更新时不会在函数中更改(react native)

nc1teljy  于 6个月前  发布在  React
关注(0)|答案(1)|浏览(82)

我有以下(简化的)react本地代码。请阅读下面的代码以获得解释。

const [id, setId] = useState(null);

const functionOne = (id) => {
    setId(id);
};

const functionTwo = (newId) => {
    if (id === newId) {
        console.log('ids are the same');
    } else {
        console.log('ids are not the same');
    }
};

useEffect(() => {
    const socket = new WebSocket(url);

    socket.addEventListener('open', () => {
        console.log('socket connection opened');
    });

    socket.addEventListener('message', (msg) => {
        const data = JSON.parse(msg.data);

        if (data.eventType === 'one') {
            functionOne(data.id);
        }

        if (data.eventType === 'two') {
            functionTwo(data.id);
        }
    }) 

    socket.addEventListener('close', () => {
        console.log('socket connection closed');
    });
}, []);

字符串
我有一个useState-hook作为ID。我有两个事件通过websockets从服务器发送到应用程序:事件“one”和事件“two”。
在事件“一”中:我向应用程序发送一个唯一的ID,并将此ID存储在useState-id-hook中。这很有效,因为当我在useState-function中执行console.log以查看状态是否更改时,它可以工作。
1分钟后,我收到了另一个事件类型为“two”的套接字消息,它的ID与我一分钟前收到的ID完全相同。我想将这个“新”ID与之前收到的ID进行比较。
问题是:当运行functionTwo()时,似乎useState-hook仍然停留在原始状态,例如“null”。它确实通过此更新得到更新,但未反映在functionTwo()中,其中“id”的值仍然是“null”。
在useCallback(functionTwo,[id])中 Package functionTwo()不起作用。
我的代码有什么问题?

编辑:

感谢T. J.克劳德,使用Redux的建议在我的情况下起作用。
我创建了一个自定义钩子,它模仿了useState钩子,但将值存储在redux中。

2ic8powd

2ic8powd1#

这是经典的function-closes-over-stale-state问题,通过functionTwo使其间接存在,* 稍微 * 模糊了一点。
问题是你的useEffect回调函数 * 关闭了 * 第一次渲染的functionTwo版本,这反过来又关闭了 * 第一次渲染的id的值。后来的idfunctionTwo版本没有被使用,因为你的useEffect依赖数组是空的。(一个好的lint插件会警告你这一点。)
有几种方法可以解决它:
1.将functionTwo作为useEffect的依赖项添加,并确保添加适当的清理代码(在从useEffect回调返回的清理函数中),这样就不会重新订阅套接字(因此在重新订阅时取消订阅)。

useEffect(() => {
    function openHandler() {
        console.log("socket connection opened");
    }
    function messageHandler(msg) {
        const data = JSON.parse(msg.data);

        if (data.eventType === "one") {
            functionOne(data.id);
        }

        if (data.eventType === "two") {
            functionTwo(data.id);
        }
    }
    function closeHandler() {
        console.log("socket connection closed");
    }

    const socket = new WebSocket(url);
    socket.addEventListener("open", openHandler);
    socket.addEventListener("message", messageHandler);
    socket.addEventListener("close", closeHandler);
    return () => {
        // NOTE: It's important to use the *same functions* when removing the handlers,
        // which is why I've separated them out from the `addEventListener` calls
        socket.removeEventListener("open", openHandler);
        socket.removeEventListener("message", messageHandler);
        socket.removeEventListener("close", closeHandler);
    };
}, [functionTwo]);

字符串
请注意,这意味着效果会在 * 每个渲染 * 上运行。您可以通过添加您为functionTwo提到的useCallback来减少它的发生频率,因此functionTwo对于任何给定的id值都是稳定的,因此(反过来)只有当id发生变化时才会产生效果。
1.添加具有最新版本idref,并在functionTwo中使用该ref,而不是直接使用id

const [id, setId] = useState(null);
const idRef = useRef(null);
idRef.current = id;

// ...

const functionTwo = (newId) => {
    if (idRef.current === newId) {
        console.log("ids are the same");
    } else {
        console.log("ids are not the same");
    }
};


这样一来,functionTwo可能是过时的就没关系了。
1.使用Redux或类似的东西来代替useState。(一个例子超出了这个答案的范围。)

相关问题