小编典典

React Hooks useState + useEffect + event给出陈旧状态

reactjs

我正在尝试将事件发射器与React
useEffect和一起使用useState,但是它始终获取初始状态而不是更新状态。如果我直接调用事件处理程序(即使使用),也可以使用setTimeout

如果我将值传递给useEffect()第二个参数,它将使其起作用,但是,每当值更改时(这是击键触发的),这都会导致事件发射器重新订阅。

我究竟做错了什么?我试过useStateuseRefuseReducer,和useCallback,并不能得到任何工作。

这是复制品:

import React, { useState, useEffect } from "react";

import { Controlled as CodeMirror } from "react-codemirror2";

import "codemirror/lib/codemirror.css";

import EventEmitter from "events";



let ee = new EventEmitter();



const initialValue = "initial value";



function App(props) {

  const [value, setValue] = useState(initialValue);



  // Should get the latest value, both after the initial server load, and whenever the Codemirror input changes.

  const handleEvent = (msg, data) => {

    console.info("Value in event handler: ", value);

    // This line is only for demoing the problem. If we wanted to modify the DOM in this event, we would instead call some setState function and rerender in a React-friendly fashion.

    document.getElementById("result").innerHTML = value;

  };



  // Get value from server on component creation (mocked)

  useEffect(() => {

    setTimeout(() => {

      setValue("value from server");

    }, 1000);

  }, []);



  // Subscribe to events on component creation

  useEffect(() => {

    ee.on("some_event", handleEvent);

    return () => {

      ee.off(handleEvent);

    };

  }, []);



  return (

    <React.Fragment>

      <CodeMirror

        value={value}

        options={{ lineNumbers: true }}

        onBeforeChange={(editor, data, newValue) => {

          setValue(newValue);

        }}

      />

      {/* Everything below is only for demoing the problem. In reality the event would come from some other source external to this component. */}

      <button

        onClick={() => {

          ee.emit("some_event");

        }}

      >

        EventEmitter (doesnt work)

      </button>

      <div id="result" />

    </React.Fragment>

  );

}



export default App;

这是具有相同代码的代码沙箱App2

https://codesandbox.io/s/ww2v80ww4l

App组件具有3种不同的实现-EventEmitter,pubsub-js和setTimeout。仅setTimeout有效。

编辑

为了阐明我的目标,我只是希望其中handleEvent的值在所有情况下都与Codemirror值匹配。单击任何按钮时,应显示当前的代码镜像值。而是显示初始值。


阅读 318

收藏
2020-07-22

共1个答案

小编典典

value在事件处理程序中是过时的,因为它从定义它的闭包中获取其值。除非每次value更改都重新订阅新的事件处理程序,否则它将不会获得新值。

解决方案1:对发布效果进行第二个论证[value]。这使事件处理程序获得正确的值,但也使效果在每次击键时再次运行。

解决方案2:使用ref将最新的存储value在组件实例变量中。然后,产生一个效果,每当value状态更改时,该效果仅更新此变量。在事件处理程序中,请使用ref,而不是value

const [value, setValue] = useState(initialValue);
const refValue = useRef(value);
useEffect(() => {
    refValue.current = value;
});
const handleEvent = (msg, data) => {
    console.info("Value in event handler: ", refValue.current);
};

https://reactjs.org/docs/hooks-faq.html#what-c​​an-i-do-if-my-effect-
dependencies-change-too-oftenten

该页面上似乎还有其他一些解决方案也可能起作用。非常感谢@Dinesh的帮助。

2020-07-22