状态不会在 useEffect 中改变

State doesn't change inside useEffect

我正在尝试使用带钩子的 websocket 并在 useEffect 上启动它。但是 states 始终是 websocket 的初始值。

我知道这是因为 useEffect 创建了一个闭包,但我没有找到解决方法。

我简化了我的代码以使其易于理解。 我需要根据activeContracts做决定,但它不会改变。 我尝试不使用 ws 作为状态,但是在新的渲染中它丢失了。

function Bitstamp() {
    const [activeContracts, setActiveContracts] = useState(["btcusd"]);
    const [ws, setWs] = useState();

    useEffect(
        () => {
            let newWs = new WebSocket("wss://ws.bitstamp.net");

            newWs.onopen = function () {
                activeContracts.map(contract => newWs.send({
                    "event": "bts:subscribe",
                    "data": {
                        "channel": "order_book_btcusd"
                    }
                }));
            };

            newWs.onmessage = function (evt) {
                console.log(activeContracts);
            };

            setWs(newWs);
        },
        [],
    );

    const handleContractChange = val => {
        setActiveContracts(val);
    };

    return (
        <ToggleButtonGroup vertical type="checkbox" value={activeContracts}
                           onChange={handleContractChange}>
            <ToggleButton
                value="btcusd">"btcusd"</ToggleButton>
            )}
            <ToggleButton
                value="btceur">"btceur"</ToggleButton>
            )}
        </ToggleButtonGroup>
    );
}

websocket 通知是否有状态变化? 我应该以其他方式初始化 websocket 还是不将其保持为状态?

更新:

我添加了一个 useEffect 跟踪 activeContracts 并更新了 onopen 和 onmessage 并且工作正常。像这样:

useEffect(
    () => {
        setWs(ws => {
            ws.onopen = function () {
                activeContracts.map(contract => newWs.send({
                    "event": "bts:subscribe",
                    "data": {
                        "channel": "order_book_btcusd"
                    }
                }));
            };

            ws.onmessage = function (evt) {
                console.log(activeContracts);
            };

            return ws;
        })
    },
    [activeContracts],
);

那是因为 useState 只在组件初始化时运行一次,因为数组参数为空。您需要在该数组中跟踪 activeContracts,但是因为 activeContracts 本身就是数组,您需要为它使用某种深度比较函数: