Estoy probando los ganchos de React por primera vez y todo parecía estar bien hasta que me di cuenta de que cuando obtengo datos y actualizo dos variables de estado diferentes (datos y marca de carga), mi componente (una tabla de datos) se representa dos veces, aunque ambas llamadas al actualizador de estado están sucediendo en la misma función. Aquí está mi función api que devuelve ambas variables a mi componente.
const getData = url => { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); useEffect(async () => { const test = await api.get('/people') if(test.ok){ setLoading(false); setData(test.data.results); } }, []); return { data, loading }; };En un componente de clase normal, haría una sola llamada para actualizar el estado, que puede ser un objeto complejo, pero el "camino de los ganchos" parece ser dividir el estado en unidades más pequeñas, cuyo efecto secundario parece ser múltiples re- renderiza cuando se actualizan por separado. ¿Alguna idea de cómo mitigar esto?
¡Esto también tiene otra solución usando useReducer ! primero definimos nuestro nuevo setState .
const [state, setState] = useReducer( (state, newState) => ({...state, ...newState}), {loading: true, data: null, something: ''} ) después de eso, simplemente podemos usarlo como las buenas clases antiguas this.setState , ¡solo que sin this !
setState({loading: false, data: test.data.results}) Como puede notar en nuestro nuevo setState (al igual que lo que teníamos anteriormente con this.setState ), ¡no necesitamos actualizar todos los estados juntos! por ejemplo, puedo cambiar uno de nuestros estados así (¡y no altera otros estados!):
setState({loading: false})Impresionante, ¿eh?
Entonces, juntemos todas las piezas:
import {useReducer} from 'react' const getData = url => { const [state, setState] = useReducer( (state, newState) => ({...state, ...newState}), {loading: true, data: null} ) useEffect(async () => { const test = await api.get('/people') if(test.ok){ setState({loading: false, data: test.data.results}) } }, []) return state }Soporte de mecanografiado . Gracias a P. Galbraith que respondió esta solución. Aquellos que usan mecanografiado pueden usar esto:
useReducer<Reducer<MyState, Partial<MyState>>>(...) donde MyState es el tipo de su objeto de estado.
por ejemplo, en nuestro caso será así:
interface MyState { loading: boolean; data: any; something: string; } const [state, setState] = useReducer<Reducer<MyState, Partial<MyState>>>( (state, newState) => ({...state, ...newState}), {loading: true, data: null, something: ''} ) Apoyo estatal anterior . En los comentarios, el usuario 2420374 solicitó una forma de tener acceso a prevState dentro de nuestro setState , así que aquí hay una forma de lograr este objetivo:
const [state, setState] = useReducer( (state, newState) => { newWithPrevState = isFunction(newState) ? newState(state) : newState return ( {...state, ...newWithPrevState} ) }, initialState ) // And then use it like this... setState(prevState => {...}) isFunction comprueba si el argumento pasado es una función (lo que significa que está intentando acceder a prevState) o un objeto simple. Puede encontrar esta implementación de isFunction por Alex Grande aquí .
aviso Para aquellos que quieren usar mucho esta respuesta, decidí convertirla en una biblioteca. Lo puedes encontrar aquí:
Actualización por lotes en ganchos de reacción https://github.com/facebook/react/issues/14259
React actualmente realizará actualizaciones de estado por lotes si se activan desde un evento basado en React, como un clic de botón o un cambio de entrada. No realizará actualizaciones por lotes si se activan fuera de un controlador de eventos de React, como una llamada asíncrona.