Considerando el siguiente ejemplo de ganchos
import { useState } from 'react'; function Example() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
Básicamente, usamos el método this.forceUpdate() para obligar al componente a volver a renderizarse inmediatamente en los componentes de la clase React como se muestra a continuación.
class Test extends Component{ constructor(props){ super(props); this.state = { count:0, count2: 100 } this.setCount = this.setCount.bind(this);//how can I do this with hooks in functional component } setCount(){ let count = this.state.count; count = count+1; let count2 = this.state.count2; count2 = count2+1; this.setState({count}); this.forceUpdate(); //before below setState the component will re-render immediately when this.forceUpdate() is called this.setState({count2: count } render(){ return (<div> <span>Count: {this.state.count}></span>. <button onClick={this.setCount}></button> </div> } }
Pero mi consulta es ¿Cómo puedo forzar el componente funcional anterior para volver a renderizar inmediatamente con ganchos?
Esto es posible con useState
o useReducer
, ya que useState
usa 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 puede abordarse de una manera más convencional.
setCount
es un ejemplo de forceUpdate
utilizado incorrectamente, setState
es asíncrono por razones de rendimiento y no debe forzarse a ser sincrónico 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 estarán actualizados. La salida del actualizador se fusiona superficialmente con el estado.
setCount
puede no ser 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 usan como devoluciones de llamada deberían 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 })); }, []);
En general, puede usar cualquier enfoque de manejo de estado que desee para activar una actualización.
const forceUpdate: () => void = React.useState()[1].bind(null, {}) // see NOTE below
const forceUpdate = React.useReducer(() => ({}), {})[1] as () => void
Solo envuelve cualquier enfoque que prefieras así
function useForceUpdate(): () => void { return React.useReducer(() => ({}), {})[1] as () => void // <- paste here }
" Desencadenar una actualización " significa decirle al motor React que algún valor ha cambiado y que debe volver a generar su componente.
[, setState]
de useState()
requiere un parámetro. Nos deshacemos de él vinculando un objeto nuevo {}
.
() => ({})
en useReducer
es un reductor ficticio que devuelve un objeto nuevo cada vez que se envía una acción.
Se requiere {}
(objeto nuevo) para que active una actualización al cambiar 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 la función entre los renderizados. 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 la igualdad de referencia entre los renders. Esto es importante si desea pasar la función forceUpdate en props.
const forceUpdate = React.useState()[1].bind(null, {}) // see NOTE above const forceUpdate = React.useReducer(() => ({}))[1]
React Hooks FAQ solución oficial para forceUpdate
:
const [_, forceUpdate] = useReducer((x) => x + 1, 0); // usage <button onClick={forceUpdate}>Force update</button>
const App = () => { const [_, forceUpdate] = useReducer((x) => x + 1, 0); return ( <div> <button onClick={forceUpdate}>Force update</button> <p>Forced update {_} times</p> </div> ); }; ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.1/umd/react.production.min.js" integrity="sha256-vMEjoeSlzpWvres5mDlxmSKxx6jAmDNY4zCt712YCI0=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.1/umd/react-dom.production.min.js" integrity="sha256-QQt6MpTdAD0DiPLhqhzVyPs1flIdstR4/R7x4GqCvZ4=" crossorigin="anonymous"></script> <script>var useReducer = React.useReducer</script> <div id="root"></div>