Estoy creando una aplicación de reacción simple que está destinada a pasar varias funciones responsables de actualizar el mismo objeto de estado en el componente principal, pero por alguna razón, solo una de las dos funciones se ejecuta cuando se activa mediante un evento onClick en un componente secundario o en al menos eso parece, déjame mostrarte algo de código:
Componente infantil:
<button id='but2' onClick={()=>{ props.isBeginner(true); props.nextStep(2) }} className={classes.chooseButton} >Choice number 1</button>Funciones del componente principal:
isBeginner(b){ setUser({...user,first_timer:b})} nextStep =(step)=>{ setUser({...user,setup_step:step})}Entre los componentes secundarios y principales, hay un componente intermedio responsable de ordenar el componente secundario correcto para representar, lo hace a través de un operador de cambio:
switch(step){ case 1 :{return <StepOne user={props.user} nextStep={props.nextStep} isBeginner={props.isBeginner} />}; break; case 2 :{return <StepTwo whatMedium={props.whatMedium} user= {props.user} nextStep={props.nextStep}/>} break; case 3 :{return <StepThree user={props.user} nextStep= {props.nextStep}/>} break; case 4 :{return <StepFourth nextStep={props.nextStep}/>} break; default:{<StepOne isBeginner={props.isBeginner} nextStep={ props.nextStep}/>} }¿Por qué no puedo actualizar el estado del componente principal usando dos funciones diferentes (transmitidas a los componentes secundarios) para actualizar diferentes valores del mismo objeto (de estado)?
Ambas funciones se ejecutan, pero parece que solo una de ellas se debe a que cada una de ellas usa el miembro de estado de user dentro del alcance, que queda obsoleto después de que se realiza la primera actualización. Entonces, la segunda actualización no incluye el cambio de la primera actualización.
Al actualizar el estado en función del estado existente, para evitar actualizaciones con información obsoleta, es mejor usar el formulario de devolución de llamada del establecedor de estado:
isBeginner(b){ setUser(user => ({...user, first_timer: b})); } // ... nextStep = (step) => { setUser(user => ({...user, setup_step: step})); };De esa forma, cada actualización de estado se realiza con un valor de estado actualizado.
Aquí hay un ejemplo más simple:
const {useState} = React; const classes = { chooseButton: "", }; const Child = (props) => { return <button id='but2' onClick={() => { props.isBeginner(true); props.nextStep(2) }} className={classes.chooseButton} > Choice number 1 </button>; }; const Wrong = () => { const [user, setUser] = useState({ first_timer: false, setup_step: 1, }); const isBeginner = (b) => { setUser({...user,first_timer:b}); }; const nextStep = (step) => { setUser(({...user,setup_step:step})); }; return <div> <h2>Wrong</h2> <div>first_timer: {String(user.first_timer)}</div> <div>step: {user.setup_step}</div> <Child isBeginner={isBeginner} nextStep={nextStep} /> </div>; }; const Right = () => { const [user, setUser] = useState({ first_timer: false, setup_step: 1, }); const isBeginner = (b) => { setUser(user => ({...user, first_timer: b})); }; const nextStep = (step) => { setUser(user => ({...user, setup_step: step})); }; return <div> <h2>Right</h2> <div>first_timer: {String(user.first_timer)}</div> <div>step: {user.setup_step}</div> <Child isBeginner={isBeginner} nextStep={nextStep} /> </div>; }; ReactDOM.render( <div> <Wrong /> <hr /> <Right /> </div>, document.getElementById("root") ); <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script> Nota al margen: esperaría una función llamada nextStep para manejar el avance al siguiente paso en lugar de aceptar un parámetro para el valor del paso. Por ejemplo:
nextStep = () => { setUser(user => ({...user, setup_step: user.setup_step + 1})); };Solo por lo que vale...