【问题标题】:React Hooks - 如何在不影响订阅的情况下使用 useState 或 useEffect?
【发布时间】:2022-01-17 11:31:28
【问题描述】:

我正在尝试使用 web3 库从 websocket url 接收数据。但是,React Hooks 让我感到沮丧,因为它在使用 useState set 函数更新我的数组时重新渲染了整个 App 函数。如何将我的渲染功能与 websocket 订阅分开?我不想在使用 setState 时每次都重新订阅。

这是我的代码:

function App() {

console.log('init');
const [blocks, setBlock] = useState([]);

(async () => {
const web3 = new Web3(new

  Web3.providers.WebsocketProvider('wss...'));

web3.eth.subscribe('newBlockHeaders', async (error, blockHeader) => {

  const block = await web3.eth.getBlock(blockHeader.hash, true);

  setBlock([...blocks, block.number]);
  console.log(blocks);

});
})();


return ( 
<div className = "App">
  <header className = "App-header">
    <p>{ blocks }</p>
  </header> 
</div>
);
}

【问题讨论】:

    标签: reactjs react-hooks use-state subscription web3


    【解决方案1】:

    使用安装 useEffect 挂钩(即 w/空依赖数组)来处理订阅。返回清理函数以取消订阅。 useEffect 挂钩运行一次 来设置订阅,并且当组件卸载 调用清理函数以退订。

    使用功能状态更新将新的块号添加到blocks 状态。当设置订阅的newBlockHeaders 事件侦听器回调时,功能状态更新避免了blocks 状态值的陈旧包围,它传递之前的状态以进行更新。

    useEffect

    useState functional updates

    function App() {
      const [blocks, setBlock] = useState([]);
    
      useEffect(() => {
        const web3 = new Web3(new Web3.providers.WebsocketProvider('wss...'));
    
        const sub = web3.eth.subscribe(
          'newBlockHeaders',
          async (error, blockHeader) => {
            const block = await web3.eth.getBlock(blockHeader.hash, true);
            setBlock(blocks => [...blocks, block.number]);
          },
        );
    
        return () => sub.unsubscribe();
      }, []);
    
      return ( 
        <div className = "App">
          <header className = "App-header">
            <p>{ blocks }</p>
          </header> 
        </div>
      );
    }
    

    【讨论】:

    • 感谢您的支持!我试图通过不影响 UseEffect 内容来避免取消订阅并更新 React 视图。有没有办法做到这一点?
    • @Logan 很抱歉,我不太关注该评论。您应该在卸载时取消订阅清理功能,否则可能会导致内存泄漏。 useEffect 钩子的标准做法是添加事件侦听器和订阅等副作用......它们应该在渲染周期之间以及在组件生命周期结束时卸载时进行清理。
    • 我想解释的是,我想连续接收数据作为流以更新 React Renderer。所以每次接收数据时,渲染器都应该根据块数组状态进行更新。但是,我注意到当我们返回 sub 时,useEffect 被一遍又一遍地调用。为什么我们不能只将渲染器与订阅分开,而不是一遍又一遍地订阅?
    • @Logan 这就是 useEffect 钩子 without 依赖项的用途。它们在组件安装、订阅时运行一次......当组件卸载时,清理订阅。事件回调处理更新状态和触发重新渲染。见timing of effects
    • 天哪.. 那么没有办法将渲染器与连续流分开吗?从 websocket 监听器接收数据时,每次都必须挂载和卸载?
    【解决方案2】:

    我认为你应该使用useCallbackuseEffect

    const subscribe = useCallback(async () => {
        const web3 = new Web3(new Web3.providers.WebsocketProvider('wss...'));
    
        web3.eth.subscribe('newBlockHeaders', async (error, blockHeader) => {
    
            const block = await web3.eth.getBlock(blockHeader.hash, true);
    
            setBlock([...blocks, block.number]);
            console.log(blocks);
    
        });
    }, [])
    
    useEffect(() => {
        subscribe()
    }, [])
    

    【讨论】:

      猜你喜欢
      • 2020-03-23
      • 1970-01-01
      • 2021-12-21
      • 2021-05-17
      • 1970-01-01
      • 1970-01-01
      • 2020-06-22
      • 1970-01-01
      • 2022-11-27
      相关资源
      最近更新 更多