¿Cómo puedo forzar que un componente se vuelva a renderizar con ganchos en React?

Resuelto Hemadri Dasari asked hace 6 años • 20 respuestas

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 forzar que el componente se vuelva a renderizar inmediatamente en los componentes de la clase React como el siguiente ejemplo.

    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 que el componente funcional anterior se vuelva a renderizar inmediatamente con ganchos?

Hemadri Dasari avatar Nov 09 '18 03:11 Hemadri Dasari
Aceptado

Esto es posible con useStateo useReducer, ya que useStatese usa useReducerinternamente :

const [, updateState] = React.useState();
const forceUpdate = React.useCallback(() => updateState({}), []);

forceUpdateno está diseñado para usarse en circunstancias normales, solo en pruebas u otros casos excepcionales. Esta situación puede abordarse de una manera más convencional.

setCountes un ejemplo de uso incorrecto forceUpdate, setStatees asincrónico por razones de rendimiento y no se debe forzar a que sea sincrónico solo porque las actualizaciones de estado no se realizaron correctamente. Si un estado depende de un estado establecido previamente, esto debe hacerse con la función de actualización .

Si necesita configurar el estado según el 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.

setCountPuede 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 es mejor memorizar las funciones que se utilizan como devoluciones de llamada:

   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 }));
   }, []);
Estus Flask avatar Nov 08 '2018 20:11 Estus Flask

Solución oficial de preguntas frecuentes de React HooksforceUpdate para :

const [_, forceUpdate] = useReducer((x) => x + 1, 0);
// usage
<button onClick={forceUpdate}>Force update</button>

Ejemplo de trabajo

Mostrar fragmento de código

ford04 avatar Jan 05 '2020 17:01 ford04

Generalmente, puede utilizar cualquier enfoque de manejo de estado que desee para activar una actualización.

Con mecanografiado

ejemplo de codesandbox

usoEstado

const forceUpdate: () => void = React.useState({})[1].bind(null, {})  // see NOTE below

useReducer (recomendado)

const forceUpdate = React.useReducer(() => ({}), {})[1] as () => void

como gancho personalizado

Simplemente envuelve el enfoque que prefieras de esta manera

function useForceUpdate(): () => void {
  return React.useReducer(() => ({}), {})[1] as () => void // <- paste here
}

¿Cómo funciona esto?

" Activar una actualización " significa decirle al motor React que algún valor ha cambiado y que debe volver a representar su componente.

[, setState]from useState()requiere un parámetro. Nos deshacemos de él uniendo un objeto nuevo {}.
() => ({})in useReduceres 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: useStatesimplemente se ajusta useReducerinternamente, así que use el reductor para reducir la complejidad. fuente

NOTA: Inestabilidad referencial

El uso .bindde with useStateprovoca un cambio en la referencia de función entre renderizados.
Es posible envolverlo por dentro useCallbackcomo ya se explicó en esta respuesta aquí , pero entonces no sería una frase sexy™ . La versión Reducer ya mantiene la igualdad de referencia (estabilidad) entre renderizados. Esto es importante si desea pasar la función forceUpdate en accesorios a otro componente.

JS simple

const forceUpdate = React.useState({})[1].bind(null, {})  // see NOTE above
const forceUpdate = React.useReducer(() => ({}))[1]
Qwerty avatar Oct 29 '2019 11:10 Qwerty

Como han mencionado los demás, useStatefunciona: así es como mobx-react-lite implementa las actualizaciones; podría 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

Brian Burns avatar Apr 26 '2019 06:04 Brian Burns