Я пытаюсь использовать источник событий с React useEffect
и useState
, но он всегда получает начальное состояние вместо обновленного состояния.Это работает, если я вызываю обработчик событий напрямую, даже с setTimeout
.
Если я передаю значение в useEffect()
2-й аргумент, это заставляет его работать, однако это вызывает повторную подписку на источник событий при каждом изменении значения (которое срабатывает при нажатии клавиш).
Что я делаю не так?Я пробовал useState
, useRef
, useReducer
и 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 во всех случаях.При нажатии любой кнопки должно отображаться текущее значение кода.Вместо этого отображается начальное значение.