Creé un componente que carga datos a través de xhr en el usuario selecciona un valor del elemento <select>.
class SomeComponent extends Component { state = { data: [], currentCategory: 'all' } switchCategory = (ev) => { console.log('Selected category is ' + ev.target.value); this.setState({ currentCategory: ev.target.value }); this.loadData(); } loadData = async () => { let { currentCategory } = this.state; // Always print previous value!!! console.log(currentCategory); // Get data via XHR... } render() { return ( <div> <select value={currentCategory} onChange={this.switchCategory}> <option value="all">All</option> {categories.map( category => <option key={category._id} value={category.category}>{category.display}</option> )} </select> <table> // ... prints data with this.state.data </table> </div> ); } }El código anterior es solo breve. El código es bastante simple, solo sincronizo un valor del elemento seleccionado con this.state.currentCategory, y detecto que está cambiando con el método switchCategory de la clase.
Pero el principal problema es que cuando accedo al estado del componente, siempre contiene un valor anterior, no un presente. Puede ver que actualizo la categoría actual en el valor de los cambios seleccionados.
switchCategory = (ev) => { console.log('Selected category is ' + ev.target.value); this.setState({ currentCategory: ev.target.value }); this.loadData(); }Entonces, en esta situación, this.state.currentCategory no debe tener "todos", como algo más "Apple", ¡pero aún contiene "todos", no un "Apple"!
loadData = async () => { let { currentCategory } = this.state; // Always print previous value!!! I expected "Apple", but it has "all" console.log(currentCategory); // Get data via XHR... }Entonces, finalmente, XHR ocurre con el valor anterior y me da datos incorrectos que no esperaba. Después de eso, al elegir otro valor de la selección (llamémoslo Banana), ¡tiene una manzana, no una banana!
Como sé, setState es un trabajo de "sincronización", por lo que llamar a this.switchCategory ocurrirá después de actualizar los estados, por lo que debe tener un valor actual, no uno anterior.
Pero cuando imprimo el estado del componente en la consola, no lo es.
Entonces, ¿qué me estoy perdiendo? ¿Por qué siempre obtengo datos antiguos, no presentes? Si estoy haciendo algo mal, ¿qué alternativas puedo hacer?
Cualquier consejo será muy apreciado. ¡Gracias!
El problema aquí es que setState es asíncrono (puede sincronizarse en ciertas situaciones). Es por eso que obtienes el valor anterior.
Hay dos soluciones posibles.
// // 1. use value directly. // switchCategory = (ev) => { this.setState({ currentCategory: ev.target.value }); this.loadData(ev.target.value); } loadData = async (currentCategory) => { console.log(currentCategory); // Get data via XHR... } // // 2. use completition callback on `setState`. // switchCategory = (ev) => { this.setState({ currentCategory: ev.target.value }, () => { this.loadData(ev.target.value); }); } loadData = async () => { const { currentCategory } = this.state; console.log(currentCategory); // Get data via XHR... } Artículo sobre setState síncrono en React [enlace]