在useEffect中使用useState值,而不进行状态更新useEffect

时间:2020-06-30 13:47:18

标签: reactjs react-hooks use-effect use-state

我正在研究一个基于对象键管理字符串数组的函数。假设它看起来像这样:

import React, { useState, useEffect } from "react";
import FieldContext from "../contexts/FieldContext";
import io from "socket.io-client";

const [socket, setSocket] = useState(null);
// the `data` array gets changed every second due to a WebSocket, in case that's important
const [data, setData] = useState({ foo: [], bar: [] });
const [connections, setConnections] = useState(["conn1", "conn2"]);

const { checkedFields } = useContext(FieldContext); // ["foo", "moo"];

useEffect(() => {
  setConnections(prevConnections => {
    // The code below does the following: 
    // Loop through the temporary connections (which is a copy of checkedFields)
    // A. if `tempConn` is a key in the `data` object, push the name with `update_` prefix to the `_conns` array
    // B. If not, just push it without a prefix to the `_conns` array
    // Since the `checkedFields` array is ["foo", "moo"], the first element will get the prefix,
    // the other won't and will just get pushed.
    let _tempConns = [...checkedFields];
    let _conns = [];
    _tempConns.forEach(tempConn => {
      if (data[tempConn] !== undefined) _conns.push(`update_${tempConn}`);
      else _conns.push(tempConn);
    });
    return _conns;
  });
}, [checkedFields]);

// the websocket hook
useEffect(() => {
  const _socket = io(WS_URI);
  _socket.on("info", data => {
    // some magic happens here to add to the `data` object which is not important for this question
  });
  setSocket(_socket);
}, [])

使用此钩子时,出现以下警告:React Hook useEffect has a missing dependency: 'data'. Either include it or remove the dependency array。据我了解,但是如果我在依赖项数组中包含data,则会得到大量不必要的更新。如何防止这种情况发生? (请不要使用// eslint-disable-next-line

3 个答案:

答案 0 :(得分:1)

不仅在data钩子的依赖项数组中缺少useEffect,而且在依赖项数组中也缺少setConnections()函数。

您可以使用useReducer挂钩将状态更新逻辑移出useEffect挂钩。

const initialState = {
   data: { foo: [], bar: [] },
   connections: ["conn1", "conn2"]
} 

const reducerFunc = (state, action) => {
   switch(action.type) {
       case 'UPDATE_CONNECTIONS':
          let _tempConns = [...action.checkedFields];
          let _conns = [];
          _tempConns.forEach(tempConn => {
              if (state.data[tempConn] !== undefined) _conns.push(`update_${tempConn}`);
              else _conns.push(tempConn);
          });
          return { ...state, connections: _conns };
        default: 
          return state;
   }
};

const [myState, dispatch] = useReducer(reducerFunc, initialState);

const { checkedFields } = useContext(FieldContext); // ["foo", "moo"];

useEffect(() => {
    dispatch({ type: 'UPDATE_CONNECTIONS', payload: checkedFields });
}, [checkedFields]);

由于React确保dispatch函数不会更改,因此可以从useEffect钩子的依赖项数组中忽略它。

有关如何使用useReducer挂钩的详细信息,请参见以下链接:

答案 1 :(得分:0)

如果您可以在网络套接字中使用以下内容,并且没有其他useEffect依赖于data参考,可以尝试使用useRef

useRef返回一个可变的ref对象,该对象的.current属性被初始化为传递的参数(initialValue)。返回的对象将在组件的整个生命周期中保持不变。

// make `data` a ref object
// Pitfall: any changes to `data.current` will NOT be tracked 
const data = useRef({ foo: [], bar: [] })


// the websocket hook
useEffect(() => {
  ...
  _socket.on("info", data => {
    ...
    // update properties on `data.current`
    data.current.foo = 'some new value'
    data.current.bar = 'anoher new value;
  });
  
  ...
}, [])

useEffect(() => {
  setConnections(prevConnections => {
    ... 
    // reference data value from data.current
    const data = data.current

    ...
    return _conns;
  });
}, [checkedFields])

答案 2 :(得分:0)

随着更新,似乎您的套接字永远不会关闭。 如果要卸载/重新安装组件,则在后台添加新的套接字。 然后在每次更新时,它会触发所有以前的套接字。

您的套接字挂钩应返回关闭套接字功能。 (我想我永远不会直接使用套接字)

  useEffect(() => {
  const _socket = io(WS_URI);
  _socket.on("info", data => {
    // some magic happens here to add to the `data` object which is not important for this question
  });
  setSocket(_socket);
  return () => {_socket.close();}
}, [])

(这不是您回答的答案,但可以帮助您:))