J'essaie d'utiliser un émetteur d'événement avec React useEffect
et useState
, mais cela obtient toujours l'état initial au lieu de l'état mis à jour. Cela fonctionne si J'appelle le gestionnaire d'événements directement, même avec un setTimeout
.
Si je passe la valeur à la fonction useEffect()
2e argument, il le fait fonctionner, mais cela provoque une nouvelle soumission à l'émetteur d'événement chaque fois que la valeur change de valeur (qui est déclenchée des frappes de frappe).
Qu'est-ce que je fais mal? J'ai essayé useState
, useRef
, useReducer
et useCallback
et ne pouviez pas avoir de travail.
Voici une reproduction:
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;
Voici une boîte à sable de code avec la même chose en App2
:
https://codesandbox.io/s/ww2v80ww4l
App
composant a 3 implémentations différentes - EventMitter, Pubsub-JS et Settimeout. Seulement des œuvres de sécurité.
Pour clarifier mon objectif, je veux simplement la valeur dans handleEvent
pour correspondre à la valeur Codemirror dans tous les cas. Lorsque vous cliquez sur n'importe quel bouton, la valeur CODEMIRROR actuelle doit être affichée. Au lieu de cela, la valeur initiale est affichée.
uSECallback aurait dû travailler ici.
import React, { useState, useEffect, useCallback } from "react";
import PubSub from "pubsub-js";
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
const handler = (msg, data) => {
console.info("Value in event handler: ", value);
document.getElementById("result").innerHTML = value;
};
const handleEvent = useCallback(handler, [value]);
// Get value from server on component creation (mocked)
useEffect(() => {
setTimeout(() => {
setValue("value from server");
}, 1000);
}, []);
// Subscribe to events on component creation
useEffect(() => {
PubSub.subscribe("some_event", handleEvent);
return () => {
PubSub.unsubscribe(handleEvent);
};
}, [handleEvent]);
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);
}}
/>
<button
onClick={() => {
ee.emit("some_event");
}}
>
EventEmitter (works)
</button>
<button
onClick={() => {
PubSub.publish("some_event");
}}
>
PubSub (doesnt work)
</button>
<button
onClick={() => {
setTimeout(() => handleEvent(), 100);
}}
>
setTimeout (works!)
</button>
<div id="result" />
</React.Fragment>
);
}
export default App;
Vérifiez la codesandbox ici https://codesandbox.io/s/react-base-forked-i9ro7