hooks icon indicating copy to clipboard operation
hooks copied to clipboard

`useWebSocket` is not available in React 18 Strict Mode

Open KONY128 opened this issue 3 years ago • 2 comments

useWebSocket is not available in React 18 Strict Mode

Environment

  • React: v18.2.0
  • React Dom: v18.2.0

Description

The hook useWebSocket is not available in React 18 Strict Mode:

  • The onOpen and other callback functions provided in parameter options are not invoked;
  • The latestMessage and readyState returned by useWebSocket hook are not updated.

In React 17 Strict Mode, none of the above problems occurred.

Demos

useWebSocket in React 18 Strict Mode - not works

useWebSocket in React 17 Strict Mode - works

Root Cause

In React 18, the concept of reusable states was introduced. (ref link)

Therefore, in the React 18 Strict Mode, all components will execute unmount and then mount again, preserving every state at the very first render to check if there's any component assuming it's only mounted or destroyed once. (official docs link)

In the source code of useWebSocket, many internal states are stored using the useRef hook. These states are initialized by the first parameter of the useRef function, which could be a problem in React 18 Strict Mode and later versions of React. Internal states like reconnectTimesRef, unmountedRef etc. will preserve the values from the last call of useWebSocket, which is not what it's expected to be.

For example, In the React 18 Strict Mode, at the very first render, the useWebSocket is called and triggered the mounting stage. Then the useWebSocket is unmounted immediately and then mounted again. At the first unmount process, the state unmountedRef is set to true, but it is not set to false again on the second mount process due to the feature of ensuring-reusable-state. So, when the real WebSocket instance connects, the state unmountedRef is true, which prevents the callback executions like onOpen and state updates like readyState.

The key is all internal states should be explicitly initialized at the mount process.

To fix this problem, I add the explicit initialization procedure of internal states in the useWebSocket at the onMount hook. Furthermore, I notice there's a hook useUnmountedRef in ahooks which handles the initialization of state unmountedRef at the mount stage. So, the state unmountedRef is created by the hook useUnmountedRef rather than explicitly handled by useMount.

All of my modifications have been tested and run correctly now in React 18 Strict Mode. The new code behaves as same as the old one in former React versions. Please review this pull request.

KONY128 avatar Oct 17 '22 09:10 KONY128

我遇到了同样的问题, 关闭严格模式后能够正常使用

miku3333 avatar Dec 03 '22 17:12 miku3333

same issue here

lake2 avatar Dec 04 '23 13:12 lake2