Solución:
Esto es posible con useState
o useReducer
, ya que useState
usos useReducer
internamente:
const [, updateState] = React.useState();
const forceUpdate = React.useCallback(() => updateState({}), []);
forceUpdate
no está diseñado para usarse en circunstancias normales, solo en pruebas u otros casos pendientes. Esta situación se puede abordar de una manera más convencional.
setCount
es un ejemplo de mal uso forceUpdate
, setState
es asíncrono por razones de rendimiento y no se debe forzar a ser síncrono solo porque las actualizaciones de estado no se realizaron correctamente. Si un estado se basa en un estado establecido previamente, esto debe hacerse con la función de actualización,
Si necesita establecer el estado en función del estado anterior, lea sobre el argumento del actualizador a continuación.
<...>
Se garantiza que tanto el estado como los accesorios recibidos por la función de actualización están actualizados. La salida del actualizador se fusiona superficialmente con el estado.
setCount
Puede que no sea un ejemplo ilustrativo porque su propósito no está claro, pero este es el caso de la función de actualización:
setCount(){
this.setState(({count}) => ({ count: count + 1 }));
this.setState(({count2}) => ({ count2: count + 1 }));
this.setState(({count}) => ({ count2: count + 1 }));
}
Esto se traduce 1: 1 a ganchos, con la excepción de que las funciones que se utilizan como devoluciones de llamada deben memorizarse mejor:
const [state, setState] = useState({ count: 0, count2: 100 });
const setCount = useCallback(() => {
setState(({count}) => ({ count: count + 1 }));
setState(({count2}) => ({ count2: count + 1 }));
setState(({count}) => ({ count2: count + 1 }));
}, []);
Como han mencionado los demás, useState
funciona – así es como mobx-react-lite implementa actualizaciones; podrías hacer algo similar.
Definir un nuevo gancho useForceUpdate
–
import { useState, useCallback } from 'react'
export function useForceUpdate() {
const [, setTick] = useState(0);
const update = useCallback(() => {
setTick(tick => tick + 1);
}, [])
return update;
}
y usarlo en un componente –
const forceUpdate = useForceUpdate();
if (...) {
forceUpdate(); // force re-render
}
Consulte https://github.com/mobxjs/mobx-react-lite/blob/master/src/utils.ts y https://github.com/mobxjs/mobx-react-lite/blob/master/src/useObserver .ts
Generalmente, puede usar cualquier enfoque de manejo de estado que desee para activar una actualización.
Con TypeScript
ejemplo de codesandbox
useState
const forceUpdate: () => void = React.useState()[1].bind(null, {}) // see NOTE below
useReducer
const forceUpdate = React.useReducer(() => ({}), {})[1] as () => void
como gancho personalizado
Simplemente envuelva el enfoque que prefiera así
function useForceUpdate(): () => void {
return React.useReducer(() => ({}), {})[1] as () => void // <- paste here
}
¿Cómo funciona esto?
“Para activar una actualización“ significa decirle al motor React que algún valor ha cambiado y que debería volver a entregar su componente.
[, setState]
de useState()
requiere un parámetro. Nos deshacemos de él atando un objeto nuevo. {}
.
() => ({})
en useReducer
es un reductor ficticio que devuelve un objeto nuevo cada vez que se envía una acción.
{}
(objeto nuevo) es necesario para que active una actualización cambiando una referencia en el estado.
PD: useState
solo envuelve useReducer
internamente. fuente
NOTA: El uso de .bind con useState provoca un cambio en la referencia de función entre las representaciones. Es posible envolverlo dentro de useCallback como ya se explicó aquí, pero entonces no sería un sexy one-liner ™. La versión Reducer ya mantiene igualdad de referencia entre renders. Esto es importante si desea pasar la función forceUpdate en props.
JS simple
const forceUpdate = React.useState()[1].bind(null, {}) // see NOTE above
const forceUpdate = React.useReducer(() => ({}))[1]