【问题标题】:Is there a way to call useEffect only once and also have access to the current state of component?有没有办法只调用一次 useEffect 并且还可以访问组件的当前状态?
【发布时间】:2020-03-06 19:26:56
【问题描述】:

我正在使用 socket.io 和 react.js。我需要为套接字声明一些事件处理程序,另外由于我正在使用一些模块,我不能使用经典的类组件,因此我将函数与钩子一起使用。问题是使用 useEffect 钩子初始化事件处理程序会导致每次发生新渲染时重新声明套接字事件处理程序,因此,每个事件都会与一堆事件处理程序连接,而不是只有一个,这会导致多次调用所需的事件发生时的事件处理程序。有办法让 useEffect 只运行一次;将一个空数组作为第二个参数传递给它(这使它像类组件中的构造函数)。这可以防止对事件处理程序的多次调用,但是,这也可以防止事件处理程序访问组件的最新状态。有没有办法同时实现?

var socket = io('http://localhost:10000');

const [var, setvar] = useState(0);
useEffect(() => {
   socket.on('event', function(data) { //event handler function
     console.log(var);
   });
}, []);

为了进一步解释,上面的代码每个事件只调用一次事件处理函数,但它总是输出 var 的初始状态,即 0。另一方面,如果 [] 参数被删除,处理程序将输出var 的当前值,但它会多次输出,因为每次渲染都有一个新的事件处理程序连接到套接字,所以输出的数量取决于在该事件之前发生的渲染次数。

【问题讨论】:

  • useEffect 回调中返回一个将调用socket.off 的函数。这将导致组件在卸载时取消注册。如果您只想在应用程序的上下文中存在 1 个到套接字的连接,您应该编写一个 Context 并在您的组件中使用它。

标签: javascript reactjs socket.io


【解决方案1】:

您可以传入“依赖项”而不是方括号,例如 [isInit]。只有当 isInit 的值发生变化时,效果才会运行。您应该使用 useState 声明 isInit 并将其默认为 false,即 const [isInit, setIsInit] = useState(false)。然后,当您第一次运行 useEffect 时调用设置 setIsInit(true)。然后在 useEffect 中检查 if (!isInit) ... 这将为您很好地控制它。

【讨论】:

  • 问题是,在那个 (!isInit) 条件下,我有两个选择:重新声明事件处理程序或不重新声明事件处理程序。在第一种情况下,会出现多次调用问题(如问题中所述),在第二种情况下,事件处理程序无权访问最新状态。
【解决方案2】:

您可以尝试将您拥有的内容与useRef 结合起来,例如:

var socket = io('http://localhost:10000');

const [var, setvar] = useState(0);
const ref = useRef({current: var});

const alteredSetvar = useCallback((var) => {
  setvar(var);
  ref.current = var;
}, [setvar, ref]);

useEffect(() => {
   socket.on('event', function(data) { //event handler function
     console.log(ref.current);
   });
}, []);

我认为可行(致电alteredSetvar 而不是setvar

useRef通常用于保存对 DOM 节点的引用,但实际上它只保存一个不可变引用,您可以根据需要更改其内部值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-13
    • 2018-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-26
    • 2011-09-26
    • 1970-01-01
    相关资源
    最近更新 更多