状态变量钩子不适用于 useEffect 回调

kmi*_*las 1 javascript state websocket reactjs react-hooks

尝试在 React 功能组件中建立 WebSocket 连接,并使用钩子更新状态变量,如下所示 [参考 1]:

export default function foo() {
    const [ arr, setArr ] = useState([])
    ws = useRef(null)

    useEffect( () => {
        ws.current = new Websocket('ws://example.com:1234')
        ws.current.onmessage = (m) => {
            setArr([...arr, m])
        })
    }, []) // Runs once at mount
}
Run Code Online (Sandbox Code Playgroud)

的状态arr未保留。它被覆盖。

注意我也尝试传递arr到效果中,就像这样,但这导致了无限循环。[参考文献 2]。随着 arr 的更新,效果被称为...

        })
    }, [arr])
}
Run Code Online (Sandbox Code Playgroud)

举一个更具体的例子,取三个 websocket 消息:

['a']
['b']
['c']
Run Code Online (Sandbox Code Playgroud)

更新后每条消息的预计到达时间

['a']
['a', 'b']
['a', 'b', 'c']
Run Code Online (Sandbox Code Playgroud)

结果:

['a']
['b']
['c']
Run Code Online (Sandbox Code Playgroud)

为什么不与回调中的钩子arr一起存储?setArr为什么是空数组?

参考

  1. 具有功能组件的 WebSocket
  2. https://github.com/facebook/react/issues/14066

Cer*_*nce 6

你有

useEffect( () => {
    ws.current = new Websocket('ws://example.com:1234')
    ws.current.onmessage = (m) => {
        setArr([...arr, m])
    })
}, []) // Runs once at mount
Run Code Online (Sandbox Code Playgroud)

事实上,那里的效果钩子在安装时运行一次- 这就是问题所在。当它运行时,附加的事件侦听器在触发时运行此行:

setArr([...arr, m])
Run Code Online (Sandbox Code Playgroud)

arr使用该处理程序范围内的值。由于处理程序仅附加一次,在挂载时, 的值arr始终相同;初始状态,空数组。

请改用回调:

setArr(arr => [...arr, m])
Run Code Online (Sandbox Code Playgroud)

我还建议声明wswith const, 以避免意外创建全局变量(并且可能调用它wsRef而不是ws,这样就不会混淆套接字的引用):

const wsRef = useRef(null)
Run Code Online (Sandbox Code Playgroud)