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.

Tuomas Toivonen
fuente
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>
  }
}
polina-c
fuente
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

François Richard
fuente
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]);
petrichor
fuente
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?

Sujan Thakare
fuente
¿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.

JamesYin
fuente
¿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}
         />
Nelles
fuente
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

tjr226
fuente
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.

Indraneel Bende
fuente
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);
satyam soni
fuente
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);
Divyanshu Rawat
fuente
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

Rajesh Nasit
fuente
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.

ibrahim alsimiry
fuente