Reaccionar - ¿Cómo forzar la representación de un componente de función?

85

Tengo un componente de función y quiero forzarlo a volver a renderizar.

¿Como lo puedo hacer?
Como no hay instancia this, no puedo llamar this.forceUpdate().

Gorakh Nath
fuente
no, un componente sin estado no tiene un estado. Use una clase en su lugar
TryingToImprove
2
¿Realmente quiere decir "componente sin estado " y no "componente funcional" ?
Chris
2
Para actualizar un componente sin estado, los accesorios pasados ​​deben cambiar.
hongoantrax
además de los accesorios, puede usar hook useState, y los componentes se volverán a renderizar cuando cambie
Hamid Shoja

Respuestas:

134

🎉 Ahora puedes, usando React hooks

Usando react hooks, ahora puede llamar useState()a su componente de función.

useState()devolverá una matriz de 2 cosas:
1. Un valor, que representa el estado actual.
2. Su armador. Úselo para actualizar el valor.

Actualizar el valor por su establecedor obligará a su componente de función a volver a renderizarse ,
al igual que lo forceUpdatehace:

import React, { useState } from 'react';

//create your forceUpdate hook
function useForceUpdate(){
    const [value, setValue] = useState(0); // integer state
    return () => setValue(value => ++value); // update the state to force render
}

function MyComponent() {
    // call your hook here
    const forceUpdate = useForceUpdate();

    return (
        <div>
            {/*Clicking on the button will force to re-render like force update does */}
            <button onClick={forceUpdate}>
                Click to re-render
            </button>
        </div>
    );
}

Puede encontrar una demostración aquí .

El componente anterior usa una función de gancho personalizada ( useForceUpdate) que usa un gancho de estado booleano ( useState). Cambia el estado booleano y, por lo tanto, le dice a React que vuelva a ejecutar la función de renderizado.

EDITAR

En una versión anterior de esta respuesta, el fragmento usaba un valor booleano y lo alternaba forceUpdate(). Ahora que he editado mi respuesta, el fragmento usa un número en lugar de un booleano.

Por qué ? (me preguntarías)

Porque una vez me sucedió que my forceUpdate()fue llamado dos veces posteriormente desde 2 eventos diferentes y, por lo tanto, se restableció el valor booleano en su estado original y el componente nunca se procesó.

Porque en el useStatesetter de ( setValueaquí), Reactcompare el estado anterior con el nuevo y renderice solo si el estado es diferente.

Yairopro
fuente
3
Nada en esa página tiene información sobre el uso de Hooks para llamar forceUpdate.
jdelman
1
Por ahora, tiene razón, es mejor, porque, incluso los ganchos difíciles aún no se han lanzado, aún puede usarlo en versión beta. Pero una vez que se publiquen, no hay razón para que el componente de clase sea mejor. El uso de ganchos hace que el código sea más limpio que el componente de clase, como muestra el siguiente video en el reactconf. De todos modos, la pregunta es si esto es posible. La respuesta ahora cambia de "No" a "Sí" debido a los ganchos. youtube.com/watch?v=wXLf18DsV-I
Yairopro
1
Hola @DanteTheSmith. Por "Nivel superior", significa que los ganchos no deben llamarse desde dentro de una condición o bucle, como dijiste. Pero puedo decirte que puedes llamarlos desde dentro de otra función. Y eso significa crear un gancho personalizado. Dan Abramov, como se presenta en el Reaccionar ganchos Reaccionar conf, demuestran claramente que esta es la manera más limpia y mejor a la lógica de acciones entre los componentes funcionales: youtu.be/dpw9EHDh2bM?t=2753
Yairopro
1
No es necesario cambiar ningún estado "para forzar el renderizado". Crea una falsa impresión de que React "compara" los valores de estado anterior y siguiente para decidir si es necesario volver a renderizar. Aunque definitivamente no es así.
meandre
1
@meandre Sí, definitivamente se compara. Estamos hablando del useStategancho, no de la clase, setStateque de hecho no hace una comparación (a menos que implementes el método shouldUpdate). Vea la misma demostración que publiqué, pero con un valor estático utilizado setState, no se vuelve a mostrar
Yairopro
47

Actualización react v16.8 (versión 16 de febrero de 2019)

Dado que react 16.8 se lanzó con ganchos , los componentes de función ahora tienen la capacidad de mantener persistente state. Con esa habilidad ahora puedes imitar un forceUpdate:

Tenga en cuenta que este enfoque debe reconsiderarse y, en la mayoría de los casos, cuando necesite forzar una actualización, probablemente esté haciendo algo mal.


Antes de reaccionar 16.8.0

No, no puede, los componentes de la función State-Less son simplemente normales functionsque regresan jsx, no tiene acceso a los métodos del ciclo de vida de React ya que no se está extendiendo desde React.Component.

Piense en el componente de función como la renderparte del método de los componentes de la clase.

Sagiv bg
fuente
ofc puedes. cambiar los accesorios que viene a él
Marián Zeke Šedaj
5
eso no es forzar una re-renderización, es solo una renderización normal. cuando desea forzar el renderizado, generalmente es un caso en el que desea ejecutar el método de renderizado cuando no fue diseñado para ejecutarse, por ejemplo, cuando no hay cambios propso nuevos state. no puede forzar la función de renderizado ya que no hay renderfunción en los componentes sin estado. Los componentes sin estado no extends React.Componentson simplemente funciones que retornan jsx.
Sagiv bg
Apoyos para "cuando necesitas forzar una actualización, probablemente estás haciendo algo mal" - Sabía que ese era el caso, pero esto me impulsó a echar un vistazo más a mi gancho useEffect.
Methodician
12

Preguntas frecuentes oficiales ( https://reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate ) ahora recomienda de esta manera si realmente necesita hacerlo:

  const [ignored, forceUpdate] = useReducer(x => x + 1, 0);

  function handleClick() {
    forceUpdate();
  }
Gramotei
fuente
Bueno, nunca lo supe
Charith Jayasanka
9
y puede hacer que su código sea 7 bytes más corto y no cree una variable no utilizada:const [, forceUpdate] = useReducer(x => x + 1, 0);
Konstantin Smolyanin
7

Usé una biblioteca de terceros llamada use-force-update para forzar la representación de mis componentes funcionales de reacción. Funcionó a la perfección. Simplemente use importar el paquete en su proyecto y utilícelo así.

import useForceUpdate from 'use-force-update';

const MyButton = () => {

  const forceUpdate = useForceUpdate();

  const handleClick = () => {
    alert('I will re-render now.');
    forceUpdate();
  };

  return <button onClick={handleClick} />;
};
Charith Jayasanka
fuente
2
Para ahorrarle un clic, useForceUpdateuse useCallbackcomo se menciona en otras respuestas. Esta lib es solo una lib de utilidad para ahorrarle pocas pulsaciones de teclas.
asyncwait
Oh gracias. Yo no lo sabía. :)
Charith Jayasanka
6

Descargo de responsabilidad: NO ES UNA RESPUESTA AL PROBLEMA.

Dejando una nota importante aquí:

Si está intentando forzar la actualización de un componente sin estado, es probable que haya algún problema con su diseño.

Considere los siguientes casos:

  1. Pase un establecedor (setState) a un componente secundario que puede cambiar de estado y hacer que el componente principal se vuelva a representar.
  2. Considere elevar el estado
  3. Considere poner ese estado en su tienda Redux, que puede forzar automáticamente un renderizado en componentes conectados.
Ankan-Zerob
fuente
1
Tengo un caso en el que necesitaba deshabilitar las actualizaciones del componente y forzarlo justo cuando quiero (es una regla en movimiento, que actualiza su valor en cada movimiento y las actualizaciones causaron un comportamiento parpadeante). Así que decidí usar clases en este componente y recomiendo que todos los componentes que necesiten un control profundo sobre el comportamiento de renderizado se hagan en clases, porque actualmente los hooks no proporcionan un control preciso sobre este comportamiento. (la versión actual de React es 16.12)
Michael Wallace
Pensé lo mismo, pero quería una solución rápida, así que usé la actualización forzada.
Charith Jayasanka
En el mundo real, tiene usos. Nada es perfecto, y si alguna vez quieres sacar el software por la puerta, es mejor que sepas cómo hacer que las cosas sucedan cuando alguien más las arruina.
Brain2000
1

Esto se puede hacer sin usar explícitamente ganchos siempre que agregue un accesorio a su componente y un estado al componente principal del componente sin estado:

const ParentComponent = props => {
  const [updateNow, setUpdateNow] = useState(true)

  const updateFunc = () => {
    setUpdateNow(!updateNow)
  }

  const MyComponent = props => {
    return (<div> .... </div>)
  }

  const MyButtonComponent = props => {
    return (<div> <input type="button" onClick={props.updateFunc} />.... </div>)
  }

  return (
    <div> 
      <MyComponent updateMe={updateNow} />
      <MyButtonComponent updateFunc={updateFunc}/>
    </div>
  )
}
Charles Goodwin
fuente
1

La respuesta aceptada es buena. Solo para que sea más fácil de entender.

Componente de ejemplo:

export default function MyComponent(props) {

    const [updateView, setUpdateView] = useState(0);

    return (
        <>
            <span style={{ display: "none" }}>{updateView}</span>
        </>
    );
}

Para forzar la re-renderización, llame al siguiente código:

setUpdateView((updateView) => ++updateView);
Manohar Reddy Poreddy
fuente