Reagire: perché il componente figlio non si aggiorna quando cambia l'elica


135

Perché nel seguente esempio di pseudo-codice Child non esegue il rendering di nuovo quando 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>
  }
}

Anche se chiamo forceUpdate()dopo aver modificato il valore in Contenitore, Child mostra comunque il vecchio valore.


5
Questo è il tuo codice? Sembra che non sia un codice React valido

Penso che il valore degli oggetti di scena non dovrebbe cambiare nel componente contenitore, ma dovrebbe essere cambiato nel componente padre da setState e che lo stato dovrebbe essere mappato agli oggetti contenitore
Piyush Patel,

Usa un operatore di diffusione come questo <Child bar = {... this.props.foo.bar} />
Sourav Singh,

@AdrianWydmanski e le altre 5 persone che hanno votato: en.wikipedia.org/wiki/Pseudocode
David Newcomb

Gli oggetti di scena @PiyushPatel vengono aggiornati quando un componente viene ridistribuito sul posto come mostra l'esempio di pseudo-codice. Un altro esempio di questo è con qualcosa come l'utilizzo <Route exact path="/user/:email" component={ListUserMessagePage} />, un collegamento nella stessa pagina aggiornerà i puntelli senza creare una nuova istanza ed eseguire i soliti eventi del ciclo di vita.
David Newcomb,

Risposte:


94

Aggiorna figlio per avere l'attributo 'chiave' uguale al nome. Il componente eseguirà nuovamente il rendering ogni volta che la chiave cambia.

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

5
Grazie per questo. Stavo usando Redux Store e non sono riuscito a ri-renderizzare il mio componente fino a quando non ho aggiunto una chiave. (Ho usato la libreria uuid per generare una chiave casuale, e ha funzionato).
Maiya,

Usando gli hook mio figlio non riattiva nuovamente quando imposta lo stato su un array esistente. La mia (probabilmente cattiva idea) mod è stato quello di impostare contemporaneamente lo stato di un oggetto di scena manichino e aggiungere che alla matrice delle dipendenze useEffect: [props.notRenderingArr, props.dummy]. Se la lunghezza dell'array cambia sempre, è possibile impostare l'array di dipendenze useEffect su [props.notRenderingArr.length].
hipnosis,

5
anche questo era ciò di cui avevo bisogno. Sono perplesso, quando è keyrichiesto, vs giusto setState? Ho dei figli che si affidano allo stato dei genitori ma non stanno innescando un nuovo rendering ...
dcsan,

Bella risposta. Ma perché questo comportamento?
Rishav,

1
Stavo usando l'indice come chiave ma non funzionava correttamente. Ora sto usando uuid ed è perfetto :) Grazie a @Maiya
Ali Rehman il

90

Perché i bambini non si renderanno se cambiano gli oggetti di scena del genitore, ma se il suo STATO cambia :)

Quello che stai mostrando è questo: https://facebook.github.io/react/tips/communicate-between-components.html

Passerà i dati da genitore a figlio attraverso oggetti di scena, ma non esiste alcuna logica di rendering.

È necessario impostare uno stato sul genitore, quindi eseguire nuovamente il rendering del figlio sullo stato di modifica del genitore. Questo potrebbe aiutare. https://facebook.github.io/react/tips/expose-component-functions.html


5
Perché setState provocherà il rendering, ma forceUpdate no? Anche poco fuori tema, ma come vengono aggiornati i componenti quando i puntelli vengono passati da Redux e il suo stato viene aggiornato attraverso l'azione?
Tuomas Toivonen,

gli oggetti di scena non vengono aggiornati anche se si aggiorna il componente gli oggetti di scena passati sono ancora lì. Flux è un altro argomento sì :)
François Richard l'

3
state! = oggetti di scena devi leggere di più al riguardo. Non si effettuano aggiornamenti con oggetti di scena
François Richard,

puoi vederlo direttamente nel codice sorgente, semplicemente non è lo stesso processo
Webwoman il

così quello che hai detto qui è uno dei due è stato modificato o non ho capito bene, ho fatto una domanda per questo tra l'altro, stackoverflow.com/questions/58903921/...
ILoveReactAndNode

51

Ho avuto lo stesso problema. Questa è la mia soluzione, non sono sicuro che sia la buona pratica, dimmi in caso contrario:

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

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

UPD: Ora puoi fare la stessa cosa usando React Hooks: (solo se il componente è una funzione)

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

3
Questa è sicuramente la risposta corretta, per non parlare della più semplice
Guilherme Ferreira,

1
Sto anche usando questo approccio.
all'incirca il

@ vancy-pants In componentDidUpdatequesto caso va nel componente Child.
Nick Friskel,

4
Non hai bisogno e non dovresti trasformare oggetti di scena da dichiarare in un componente figlio. Usa direttamente gli oggetti di scena. In FunctionComponentsesso aggiornerà automaticamente i componenti figlio se cambiano gli oggetti di scena.
oemera,

1
Questo approccio funziona anche quando hai oggetti di scena in componenti figlio derivati ​​dallo stato genitore
Chefk5

10

Secondo la filosofia di React, il componente non può cambiare i suoi oggetti di scena. dovrebbero essere ricevuti dal genitore e dovrebbero essere immutabili. Solo un genitore può cambiare gli oggetti di scena dei suoi figli.

bella spiegazione su stato vs oggetti di scena

leggi anche questa discussione Perché non riesco ad aggiornare gli oggetti di scena in React.js?


Questo non significa anche che il container non può modificare gli oggetti di scena che riceve dal negozio di Redux?
Tuomas Toivonen,

3
Il contenitore non riceve gli oggetti di scena direttamente dal negozio, vengono forniti leggendo una parte di un albero di stato redux (confuso ??). !! la confusione è perché non conosciamo la funzione magica connessa da reattivo-redux. se vedi il codice sorgente del metodo connect, fondamentalmente crea un componente di ordine superiore che ha uno stato con storeState di proprietà ed è iscritto all'archivio redux. man mano che storeState cambia l'intero rendering, gli oggetti di scena modificati vengono forniti al contenitore leggendo lo stato modificato. spero che questo risponda alla domanda
Sujan Thakare

Buona spiegazione, pensare alla componente di ordine superiore mi aiuta a capire cosa sta succedendo
Tuomas Toivonen,

7

Dovresti usare la setStatefunzione. Altrimenti, state non salverà le modifiche, indipendentemente da come usi 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>
        }
    }
}

Il tuo codice non sembra valido. Non riesco a testare questo codice.


Non dovrebbe essere <Child bar={this.state.foo.bar} />?
Josh Broadhurst,

Destra. Aggiorno la risposta. Ma come ho già detto, questo potrebbe non essere un codice valido. Basta semplicemente copiare dalla domanda.
JamesYin,

5

Quando si creano componenti React da functionse useState.

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

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

Questo non funziona

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

Questo funziona (aggiunta chiave)

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

3

Confermato, l'aggiunta di una chiave funziona. Ho esaminato i documenti per cercare di capire il perché.

React vuole essere efficiente durante la creazione di componenti figlio. Non renderà un nuovo componente se è uguale a un altro figlio, il che rende il caricamento della pagina più veloce.

L'aggiunta di una chiave costringe React a renderizzare un nuovo componente, ripristinando così lo stato per quel nuovo componente.

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


2

Utilizzare la funzione setState. Quindi potresti farlo

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

all'interno del metodo dell'evento handle.

Una volta, lo stato viene aggiornato, attiverà le modifiche e verrà eseguito nuovamente il rendering.


1
Dovrebbe essere this.setState ({foo.bar:123}) giusto?
Charith Jayasanka,

0

Probabilmente dovresti rendere Child come componente funzionale se non mantiene alcuno stato e semplicemente esegue il rendering degli oggetti di scena e quindi lo chiama dal genitore. In alternativa a ciò è possibile utilizzare gli hook con il componente funzionale (useState) che provocherà il rendering del componente senza stato.

Inoltre, non dovresti alterare i Propa in quanto sono immutabili. Mantiene lo stato del componente.

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

0

Stavo riscontrando lo stesso problema. Avevo un Tooltipcomponente che riceveva showTooltipprop, che stavo aggiornando sul Parentcomponente in base a una ifcondizione, che veniva aggiornato nel Parentcomponente ma che il Tooltipcomponente non stava eseguendo il rendering.

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

L'errore che stavo facendo è dichiarare showTooltipun permesso.

Mi sono reso conto che cosa stavo facendo di sbagliato stavo violando i principi di come funziona il rendering, sostituendolo con ganci ha fatto il lavoro.

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

0

definire oggetti di scena modificati in mapStateToProps del metodo connect nel componente figlio.

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

export default connect(mapStateToProps)(ChannelItem);

Nel mio caso, il canale di channelList viene aggiornato, quindi ho aggiunto chanelList in 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}
    />
  );
}

questo esempio usa useEffectper cambiare stato quando propscambia.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.