【问题标题】:Keep state in sync with fast occuring events使状态与快速发生的事件同步
【发布时间】:2020-11-25 10:46:50
【问题描述】:

我制作了一个自定义挂钩来跟踪基于接收到的套接字事件数量的状态变量。 当我通过发送 10 个同时事件进行测试时,状态变量总数的结果是 6 或 7 或 8,而不是预期的 10。

const useSocketAmountReceivedState = (event: string): number => {
    const [total, setTotal] = useState(0);

    useEffect(() => {
        Socket.on(event, () => {
            console.log(total);
            setTotal(total + 1);
        });

        return (): void => {
            Socket.off(event);
        };
    });

    return total;
}

运行日志看起来像

0
1
1
2
3
3
4
4
4
5

上面例子中的Socket是围绕websocket实现的。

所以我可以推断总更新速度不够快,但是处理这种行为的最佳模式是什么?

【问题讨论】:

  • 我不确定这是否会有所帮助,但请尝试:setTotal(prevTotal => prevTotal + 1);
  • Socket.on 事件必须在 useEffect 函数之外
  • 请确保不要在每次渲染时运行 useEffect()。为此,在 useEffect() 中为第二个参数添加一个空数组。
  • 感谢@EdsonMagombe 的快速回复,这成功了,你能解释一下为什么吗?是因为useEffect 只会在状态更改后再次“更新”total 吗?
  • 没有。这很简单。每次调用 useEffect 时,您都在创建一个事件 Socket.on。所以你有很多 Socket.on 打印

标签: javascript reactjs typescript sockets react-hooks


【解决方案1】:

Socket.on 事件必须在 useEffect 函数之外

const useSocketAmountReceivedState = (event: string): number => {
    const [total, setTotal] = useState(0);
    Socket.on(event, () => {
        console.log(total);
        setTotal(total + 1);
    });
    useEffect(() => {
        return (): void => {
            Socket.off(event);
        };
    }, []);

    return total;
}

【讨论】:

  • 虽然这有点用,但它也经常调用on 函数。太多实际上不断地重新绑定它,这会对性能造成巨大影响,而且似乎没有必要。
  • 请注意,您必须将其设置在仅调用一次的位置。如果您多次使用您的 useSocketAmountReceivedState 。这将使事件成倍增加
  • 所以你必须把它移到你确保它只运行一次的地方。如果您分享您的代码,我可能会有所帮助
  • ``` const Demo = (): ReactElement => { const topicUpdates = useSocketAmountReceivedState('topic-test');返回 (
    {topicUpdates}
    ) }; ```你会建议包装它吗?
  • 没有。没关系。你有主课吗?在同一个文件中?
【解决方案2】:

尝试将一个空数组作为钩子的第二个参数。您不希望它在每次组件呈现时都注册一个事件。

const useSocketAmountReceivedState = (event: string): number => {
    const [total, setTotal] = useState(0);

    useEffect(() => {
        Socket.on(event, () => {
            console.log(total);
            setTotal(total + 1);
        });

        return (): void => {
            Socket.off(event);
        };
    }, [total]);

    return total;
}

更新:

我对最初的答案进行了更新,并将 Total 添加到 React Hook 的依赖数组中。

注意第二个参数,也就是依赖数组。它是一个接受状态或道具的数组。并且它指示 React 在每次依赖数组中的任何元素发生变化时运行这个钩子。

如果你传递一个空数组,那么钩子只会在初始加载时运行,在组件挂载之后。

在您的示例中,如果您传递一个空数组,它会创建一个闭包。因此,总值永远是初始值。

因此您可以将 Total 传递到依赖数组中,该数组将调用 useEffect() 以仅在 Total 值更改时运行。在您的示例中,没有传递给第二个参数的依赖数组,useEffect() 将每次运行,这不是我们想要的。

【讨论】:

  • 通过查看问题,我会说提问者可能不确定依赖项是如何工作的,因此可能值得更新您的答案来解释这一点,并解释 useEffect 函数将在每次依赖项更改时执行,并且几乎所有时间(至少根据我的经验)不放置任何依赖项(即方括号)将导致 useEffect 无限期地一遍又一遍地继续执行。
  • 添加了更多细节:)
  • 我刚刚测试了这种方法。在这种情况下,我相信0total 的初始值包含在回调中。所以这导致 total 每次更新为 1。
  • 这和原来有同样的问题,total没有像它应该的那样频繁地“改变”,落后了。
【解决方案3】:

一位同事提出了这个解决方案。 使用未包含引用的引用。

const useSocketAmountReceivedState = (event: string): number => {
    const count = useRef(0);
    const [state, setState] = useState(0);

    useEffect(() => {
        Socket.on(event, () => {
            count.current++;
            setState(count.current);
        });

        return (): void => {
            Socket.off(event);
        };
    }, []);

    return state;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-21
    • 2021-04-14
    • 2018-10-12
    • 1970-01-01
    • 2021-05-15
    • 1970-01-01
    • 2021-09-05
    相关资源
    最近更新 更多