Reaccionar: por qué el componente hijo no se actualiza cuando el accesorio cambia


135

¿Por qué en el siguiente ejemplo de pseudocódigo Child no se vuelve a procesar cuando Container cambia foo.bar?

Container {
  handleEvent() {
    this.props.foo.bar = 123
  },

  render() {
    return <Child bar={this.props.foo.bar} />
}

Child {
  render() {
    return <div>{this.props.bar}</div>
  }
}

Incluso si llamo forceUpdate()después de modificar el valor en Contenedor, Child aún muestra el valor anterior.


55
¿Es este tu código? Parece que no es un código de

Creo que el valor apoyos no debe cambiar en el componente contenedor sino que debe ser el cambio en el componente de los padres por setstate y que el estado debe ser un mapa de los puntales Contenedores
Piyush Patel

Utilice el operador de propagación como este <Child bar = {... this.props.foo.bar} />
Sourav Singh

@AdrianWydmanski y las otras 5 personas que votaron: en.wikipedia.org/wiki/Pseudocode
David Newcomb

Los accesorios de @PiyushPatel se actualizan cuando un componente se vuelve a representar en el lugar, como muestra el ejemplo de pseudocódigo. Otro ejemplo de esto es con algo como usar <Route exact path="/user/:email" component={ListUserMessagePage} />, un enlace en la misma página actualizará los accesorios sin crear una nueva instancia y ejecutar los eventos habituales del ciclo de vida.
David Newcomb

Respuestas:


94

Actualice el hijo para que tenga el atributo 'clave' igual al nombre. El componente se volverá a procesar cada vez que cambie la clave.

Child {
  render() {
    return <div key={this.props.bar}>{this.props.bar}</div>
  }
}

55
Gracias por esto. Estaba usando la tienda redux y no pude hacer que mi componente se volviera a procesar hasta que agregué una clave. (Usé la biblioteca uuid para generar una clave aleatoria, y funcionó).
Maiya el

Al usar ganchos, mi hijo no volvería a renderizar al establecer el estado en una matriz existente. Mi (probablemente mala idea) truco era establecer simultáneamente el estado de un apoyo ficticio y añadir que a la matriz de dependencia useEffect: [props.notRenderingArr, props.dummy]. Si la longitud de la matriz siempre cambia, puede establecer la matriz de dependencia useEffect en [props.notRenderingArr.length].
hipnosis

55
Esto era lo que yo también necesitaba. Estoy desconcertado, cuando es un keyrequisito, frente a solo setState. Tengo algunos hijos que confían en el estado de los padres, pero no están activando una nueva representación ...
dcsan

Gran respuesta. ¿Pero por qué este comportamiento?
Rishav

1
Estaba usando el índice como clave pero no funcionaba correctamente. Ahora estoy usando uuid y es perfecto :) Gracias a @Maiya
Ali Rehman

90

Porque los niños no vuelven a rendirse si los accesorios del padre cambian, pero si su ESTADO cambia :)

Lo que está mostrando es esto: https://facebook.github.io/react/tips/communicate-between-components.html

Pasará datos de padre a hijo a través de accesorios, pero no hay lógica de reenvío allí.

Debe establecer algún estado para el padre y luego volver a entregar el niño en el estado de cambio de padre. Esto podria ayudar. https://facebook.github.io/react/tips/expose-component-functions.html


55
¿Por qué es que setState causará el re-render pero forceUpdate no? También un poco fuera de tema, pero ¿cómo se actualizan los componentes cuando los accesorios se pasan de redux y su estado se actualiza a través de la acción?
Tuomas Toivonen

los accesorios no se actualizan, incluso si actualiza el componente, los accesorios que pasó todavía están allí. El flujo es otro tema sí :)
François Richard

3
state! = accesorios necesita leer más sobre eso. No haces actualizaciones con accesorios
François Richard

puedes verlo directamente en el código fuente, simplemente no es el mismo proceso
Webwoman

entonces, lo que dijiste aquí ha cambiado o no lo entendí correctamente, por cierto, hice una pregunta para eso, stackoverflow.com/questions/58903921/…
ILoveReactAndNode

51

Yo tuve el mismo problema. Esta es mi solución, no estoy seguro de que sea una buena práctica, dime si no:

state = {
  value: this.props.value
};

componentDidUpdate(prevProps) {
  if(prevProps.value !== this.props.value) {
    this.setState({value: this.props.value});
  }
}

UPD: ahora puede hacer lo mismo con React Hooks: (solo si el componente es una función)

const [value, setValue] = useState(propName);
// This will launch only if propName value has chaged.
useEffect(() => { setValue(propName) }, [propName]);

3
Esta es definitivamente la respuesta correcta, sin mencionar la más simple
Guilherme Ferreira

1
También estoy usando este enfoque.
rawly

@ vancy-pants El componentDidUpdateen este caso va en el componente Child.
Nick Friskel el

44
No necesita y no debe transformar los accesorios al estado en un componente secundario. Solo usa los accesorios directamente. En FunctionComponentsél, se actualizarán automáticamente los componentes secundarios si cambian los accesorios.
oemera

1
Este enfoque también funciona cuando tiene accesorios en componentes secundarios derivados del estado primario
Chefk5

10

Según la filosofía React, el componente no puede cambiar sus accesorios. deben recibirse de los padres y deben ser inmutables. Solo los padres pueden cambiar los accesorios de sus hijos.

buena explicación sobre estado vs accesorios

también, lea este hilo ¿Por qué no puedo actualizar los accesorios en react.js?


¿No significa esto también que el contenedor no puede modificar los accesorios que recibe de la tienda redux?
Tuomas Toivonen

3
El contenedor no recibe los accesorios directamente de la tienda, se suministran leyendo una parte de un árbol de estado redux (¿confuso?). La confusión se debe a que no conocemos la función mágica de conectar por react-redux. si ve el código fuente del método de conexión, básicamente crea un componente de orden superior que tiene un estado con la propiedad storeState y está suscrito a la tienda redux. A medida que storeState cambia, se produce una nueva representación completa y los accesorios modificados se suministran al contenedor leyendo el estado modificado. Espero que esto responda la pregunta
Sujan Thakare

Buena explicación, pensando en el componente de orden superior me ayuda a entender lo que está sucediendo
Tuomas Toivonen

7

Deberías usar la setStatefunción. De lo contrario, el estado no guardará su cambio, sin importar cómo use forceUpdate.

Container {
    handleEvent= () => { // use arrow function
        //this.props.foo.bar = 123
        //You should use setState to set value like this:
        this.setState({foo: {bar: 123}});
    };

    render() {
        return <Child bar={this.state.foo.bar} />
    }
    Child {
        render() {
            return <div>{this.props.bar}</div>
        }
    }
}

Tu código parece no válido. No puedo probar este código.


¿No debería ser <Child bar={this.state.foo.bar} />?
Josh Broadhurst

Correcto. Actualizo la respuesta. Pero como dije, este puede no ser un código válido. Simplemente copie de la pregunta.
JamesYin

5

Cuando cree componentes React de functionsy useState.

const [drawerState, setDrawerState] = useState(false);

const toggleDrawer = () => {
      // attempting to trigger re-render
      setDrawerState(!drawerState);
};

Esto no funciona

         <Drawer
            drawerState={drawerState}
            toggleDrawer={toggleDrawer}
         />

Esto funciona (agregando clave)

         <Drawer
            drawerState={drawerState}
            key={drawerState}
            toggleDrawer={toggleDrawer}
         />

3

Confirmado, agregar una clave funciona. Revisé los documentos para tratar de entender por qué.

React quiere ser eficiente al crear componentes secundarios. No representará un nuevo componente si es igual a otro elemento secundario, lo que hace que la página se cargue más rápido.

Agregar una clave obliga a React a representar un nuevo componente, restableciendo así el estado para ese nuevo componente.

https://reactjs.org/docs/reconciliation.html#recursing-on-children


2

Use la función setState. Entonces podrías hacer

       this.setState({this.state.foo.bar:123}) 

dentro del método de evento de manejo.

Una vez que se actualiza el estado, se activarán los cambios y se volverá a procesar.


1
Debería ser this.setState ({foo.bar:123}) ¿verdad?
Charith Jayasanka

0

Probablemente debería hacer que el Child sea un componente funcional si no mantiene ningún estado y simplemente presenta los accesorios y luego lo llama desde el padre. Una alternativa a esto es que puede usar ganchos con el componente funcional (useState) que hará que el componente sin estado se vuelva a representar.

Además, no debe alterar los propas ya que son inmutables. Mantener el estado del componente.

Child = ({bar}) => (bar);

0

Me encontraba con el mismo problema. Tenía un Tooltipcomponente que estaba recibiendo showTooltipapoyo, que estaba actualizando el Parentcomponente en función de una ifcondición, se estaba actualizando en el Parentcomponente pero el Tooltipcomponente no se estaba procesando.

const Parent = () => {
   let showTooltip = false;
   if(....){ showTooltip = true; }
   return(
      <Tooltip showTooltip={showTooltip}></Tooltip>
   )
}

El error que estaba haciendo es declarar showTooltip como un let.

Me di cuenta de lo que estaba haciendo mal. Estaba violando los principios de cómo funciona el renderizado. Reemplazarlo con ganchos hizo el trabajo.

const [showTooltip, setShowTooltip] =  React.useState<boolean>(false);

0

definir accesorios modificados en mapStateToProps del método de conexión en el componente hijo.

function mapStateToProps(state) {
  return {
    chanelList: state.messaging.chanelList,
  };
}

export default connect(mapStateToProps)(ChannelItem);

En mi caso, el canal de channelList se actualiza, así que agregué chanelList en mapStateToProps


0
export default function DataTable({ col, row }) {
  const [datatable, setDatatable] = React.useState({});
  useEffect(() => {
    setDatatable({
      columns: col,
      rows: row,
    });
  /// do any thing else 
  }, [row]);

  return (
    <MDBDataTableV5
      hover
      entriesOptions={[5, 20, 25]}
      entries={5}
      pagesAmount={4}
      data={datatable}
    />
  );
}

este ejemplo se usa useEffectpara cambiar el estado cuando se propscambia.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.