状态变量挂钩无法与useEffect回调一起使用

时间:2020-09-21 20:46:15

标签: javascript reactjs websocket react-hooks state

尝试在react功能组件中建立Websocket连接,并使用钩子更新状态变量,例如[Ref 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
}

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

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

        })
    }, [arr])
}

对于一个更具体的示例,请采取以下三种websocket消息:

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

更新后,每封邮件的预期误码

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

结果:

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

为什么arr没有与setArr挂钩一起存储在回调中?为什么它是一个空数组?

参考

  1. WebSockets with functional components
  2. https://github.com/facebook/react/issues/14066

2 个答案:

答案 0 :(得分:2)

你有

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

实际上,效果挂钩在挂载上运行一次-就是问题所在。运行时,附加的事件侦听器在触发时运行以下行:

setArr([...arr, m])

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

改为使用回调:

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

我还建议您将ws声明为const,以避免意外创建全局变量(并且可能将其命名为wsRef而不是ws,所以您不必不会混淆套接字的引用):

const wsRef = useRef(null)

答案 1 :(得分:1)

您可以尝试将数组的先前状态改为:

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

对于setState,有两种处理更改的方法,第一种是提供值作为参数,第二种方法是传递回调函数。它称为更新程序功能,它提供状态的先前值。进一步了解setState here