简体   繁体   English

State 变量挂钩不适用于 useEffect 回调

[英]State variable hook not working with useEffect callback

Attempting to set up a websocket connection within a react functional component, and update a state variable with a hook, like so [Ref 1]:尝试在 React 功能组件中建立 websocket 连接,并使用钩子更新 state 变量,如 [参考文献 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
}

The state of arr is not preserved. arr的 state 没有保留。 It is overwritten.它被覆盖了。

NOTE I also tried passing arr into the effect, like so, but this resulted in an endless loop.注意我也试过将arr传递给效果,就像这样,但这导致了无限循环。 [Ref 2]. [参考文献 2]。 As arr was updated, the effect was called...随着 arr 的更新,效果被称为...

        })
    }, [arr])
}

For a more specific example, take three websocket messages:举个更具体的例子,取三个 websocket 消息:

['a']
['b']
['c']

Expected arr on each message, after update更新后每条消息的预期 arr

['a']
['a', 'b']
['a', 'b', 'c']

Result:结果:

['a']
['b']
['c']

Why isn't arr stored with the setArr hook in the callback?为什么arr不与回调中的setArr挂钩一起存储? Why is it an empty array?为什么它是一个空数组?

References参考

  1. WebSockets with functional components 具有功能组件的 WebSockets
  2. https://github.com/facebook/react/issues/14066 https://github.com/facebook/react/issues/14066

You have你有

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

Indeed, the effect hook there runs once on mount - and that's the problem.事实上,那里的效果挂钩在挂载上运行一次——这就是问题所在。 When it runs, the event listener that is attached, when triggered, runs this line:当它运行时,附加的事件侦听器在被触发时运行以下行:

setArr([...arr, m])

using the value of arr which is in scope for that handler.为该处理程序使用 scope 中的arr值。 Since the handler is attached only once, on mount, the value of arr is always the same;由于处理程序仅附加一次,在挂载时, arr的值始终相同; the initial state, the empty array.初始 state,空数组。

Use the callback instead:请改用回调:

setArr(arr => [...arr, m])

I'd also recommend declaring the ws with const , to avoid accidentally creating a global variable (and maybe call it wsRef rather than ws , so you don't confuse the ref for the socket):我还建议用const声明ws ,以避免意外创建全局变量(并且可能将其wsRef而不是ws ,这样您就不会混淆套接字的 ref ):

const wsRef = useRef(null)

You can try to use the previous state of your array instead as:您可以尝试使用数组的前一个 state 代替:

ws.current.onmessage = (m) => {
   setArr(prevState => [...prevState, m])
}

For setState there are two approaches how you can handle the change, the first is to give a value as a parameter , the second option is the pass a callback function .对于setState有两种方法可以处理更改,第一种是给一个值作为参数,第二种是传递回调 function It's called updater function which provides the previous value of the state. Read further about setState here .它称为更新程序 function,它提供 state 的先前值。在此处进一步了解setState

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM