¿Por qué mi useEffect no se dispara al actualizar una dependencia que usa un contexto React? Estoy usando un contexto como parte de un asistente que recopilará una gran cantidad de datos de un usuario y luego los procesará en el último paso del asistente.
Los campos de Person se están actualizando y en la siguiente página del asistente puedo recuperarlos (no se muestra en mi ejemplo por brevedad). Sin embargo, quiero hacer una llamada API en useEffect para realizar algún procesamiento después de que el usuario haya ingresado su nombre y dirección.
Este es el código (es un poco artificial):
export interface Person { name: string; address: string; skills: string[]; // collected on a different wizard page }; const PersonContext = React.createContext(null); const PersonProvider: React.FC = ({children}) => { const [personState, setPersonState] = useState<Person>({ name: "", address: "" }); return ( <PersonContext.Provider value={personState}> {children} </PersonContext.Provider> ); }; export const PersonPage: React.FC<{}> = () => { const personState = useContext(PersonContext); useEffect(() => { console.log("useEffect"); }, [personState]); /* Also tried, but no joy: useEffect(() => { console.log("useEffect"); }, [personState.name, personState.address]); */ const onNameChanged = (e) => { if(event.key === "Enter") { console.log(`name: ${e.target.value}`); personState.name = e.target.value; } }; const onAddressChanged = (e) => { if(event.key === "Enter") { console.log(`name: ${e.target.value}`); personState.address = e.target.value; } }; return ( <div> Name: <input name="name" onKeyDown={(e) => onNameChanged(e)} /> (press enter) <br /> You typed: <span>{personState.name}</span> <br/> <br/> Address: <input name="address" onKeyDown={(e) => onAddressChanged(e)} /> (press enter) <br /> You typed: <span>{personState.address}</span> </div> ); }; export const App: React.FC<{}> = () => { return ( <PersonProvider> <PersonPage /> </PersonProvider> ); }También tengo un ejemplo de CodePen aquí:
Estás mutando el objeto. Debe usar la función setState para actualizarlo, por ejemplo.
en vez de
const onAddressChanged = (e) => { if(event.key === "Enter") { personState.address = e.target.value; } };hacer esto
const onAddressChanged = (e) => { if(event.key === "Enter") { setPersonState(prevState => {...prevState, address: e.target.value)} } };const PersonProvider: React.FC = ({children}) => { // you should expose both the state object and its setter const personGetterSetter = useState<Person>({ name: "", address: "" }); return ( <PersonContext.Provider value={personGetterSetter}> {children} </PersonContext.Provider> ); }; export const PersonPage: React.FC<{}> = () => { // unpack them here const [personState, setPersonState] = useContext(PersonContext); useEffect(() => { console.log("useEffect"); }, [personState]); const onNameChanged = (e) => { if(event.key === "Enter") { console.log(`name: ${e.target.value}`); // Wrong way to go // do not mutate state object directly // personState.name = e.target.value; // use the setter, this is the react way to update state setPersonState(personState => { return { ...personState, name: e.target.value } }) } }; ... };