Wie aktualisiere ich den Status der Eltern in React?

349

Meine Struktur sieht wie folgt aus:

Component 1  

 - |- Component 2


 - - |- Component 4


 - - -  |- Component 5  

Component 3

Komponente 3 sollte je nach Status von Komponente 5 einige Daten anzeigen. Da Requisiten unveränderlich sind, kann ich ihren Status nicht einfach in Komponente 1 speichern und weiterleiten, oder? Und ja, ich habe über Redux gelesen, möchte es aber nicht verwenden. Ich hoffe, dass es möglich ist, es nur mit reagieren zu lösen. Liege ich falsch?

wklm
quelle
20
super einfach: Übergeben Sie die übergeordnete setState-Funktion über die Eigenschaft an die untergeordnete Komponente: <MyChildComponent setState = {p => {this.setState (p)}} /> Rufen Sie sie in der untergeordneten Komponente über this.props auf. setState ({myObj, ...});
Marcel Ennix
@MarcelEnnix, Dein Kommentar spart mir Zeit. Vielen Dank.
Dinith Minura
<MyChildComponent setState={(s,c)=>{this.setState(s, c)}} />Wenn Sie diesen Hack verwenden, stellen Sie sicher, dass Sie den Rückruf unterstützen.
Barkermn01
4
Das Übergeben eines Rückrufs zum Festlegen des Status der Eltern ist eine wirklich schlechte Praxis, die zu Wartungsproblemen führen kann. Es unterbricht die Kapselung und macht die Komponenten 2, 4 und 5 eng mit 1 verbunden. Wenn Sie diesen Pfad gehen, können Sie keine dieser untergeordneten Komponenten an anderer Stelle wiederverwenden. Es ist besser, wenn Sie bestimmte Requisiten haben, damit untergeordnete Komponenten Ereignisse auslösen können, wenn etwas passiert. Dann würde die übergeordnete Komponente dieses Ereignis ordnungsgemäß behandeln.
Pato Loco
@MarcelEnnix, warum die geschweiften Klammern herum this.setState(p)? Ich habe es ohne sie versucht und es scheint zu funktionieren (ich bin sehr neu in React)
Biganon

Antworten:

677

Für die Kind-Eltern-Kommunikation sollten Sie eine Funktion übergeben, die den Status von Elternteil zu Kind wie folgt festlegt

class Parent extends React.Component {
  constructor(props) {
    super(props)

    this.handler = this.handler.bind(this)
  }

  handler() {
    this.setState({
      someVar: 'some value'
    })
  }

  render() {
    return <Child handler = {this.handler} />
  }
}

class Child extends React.Component {
  render() {
    return <Button onClick = {this.props.handler}/ >
  }
}

Auf diese Weise kann das Kind den Status des Elternteils mit dem Aufruf einer mit Requisiten übergebenen Funktion aktualisieren.

Sie müssen jedoch die Struktur Ihrer Komponenten überdenken, da die Komponenten 5 und 3 meines Wissens nicht miteinander zusammenhängen.

Eine mögliche Lösung besteht darin, sie in eine Komponente höherer Ebene zu verpacken, die den Status von Komponente 1 und 3 enthält. Diese Komponente setzt den Status niedrigerer Ebene durch Requisiten.

Ivan
quelle
6
Warum brauchen Sie this.handler = this.handler.bind (this) und nicht nur die Handlerfunktion, die den Status festlegt?
Chemook78
34
@ chemook78 in ES6 React-Klassenmethoden binden nicht automatisch an die Klasse. Wenn wir also keinen this.handler = this.handler.bind(this)Konstruktor hinzufügen , verweist die Funktion thisinnerhalb der handlerFunktion auf den Funktionsabschluss und nicht auf die Klasse. Wenn Sie nicht alle Ihre Funktionen im Konstruktor binden möchten, gibt es zwei weitere Möglichkeiten, mit Pfeilfunktionen damit umzugehen. Sie können den Klick-Handler einfach als schreiben onClick={()=> this.setState(...)}oder Eigenschaftsinitialisierer zusammen mit Pfeilfunktionen verwenden, wie hier beschrieben. Babeljs.io/blog/2015/06/07/react-on-es6-plus unter " Pfeilfunktionen "
Ivan
1
Hier ist ein Beispiel dafür in Aktion: plnkr.co/edit/tGWecotmktae8zjS5yEr?p=preview
Tamb
5
Das alles macht Sinn, warum e.preventDefault? und erfordert das jquery?
Vincent Buscarello
1
Kurze Frage, verbietet dies die Zuweisung eines lokalen Staates innerhalb des Kindes?
ThisGuyCantEven
50

Ich habe die folgende funktionierende Lösung gefunden, um das onClick-Funktionsargument vom Kind an die übergeordnete Komponente zu übergeben:

Version mit Übergabe einer Methode ()

//ChildB component
class ChildB extends React.Component {

    render() {

        var handleToUpdate  =   this.props.handleToUpdate;
        return (<div><button onClick={() => handleToUpdate('someVar')}>
            Push me
          </button>
        </div>)
    }
}

//ParentA component
class ParentA extends React.Component {

    constructor(props) {
        super(props);
        var handleToUpdate  = this.handleToUpdate.bind(this);
        var arg1 = '';
    }

    handleToUpdate(someArg){
            alert('We pass argument from Child to Parent: ' + someArg);
            this.setState({arg1:someArg});
    }

    render() {
        var handleToUpdate  =   this.handleToUpdate;

        return (<div>
                    <ChildB handleToUpdate = {handleToUpdate.bind(this)} /></div>)
    }
}

if(document.querySelector("#demo")){
    ReactDOM.render(
        <ParentA />,
        document.querySelector("#demo")
    );
}

Schauen Sie sich JSFIDDLE an

Version mit Übergabe einer Pfeilfunktion

//ChildB component
class ChildB extends React.Component {

    render() {

        var handleToUpdate  =   this.props.handleToUpdate;
        return (<div>
          <button onClick={() => handleToUpdate('someVar')}>
            Push me
          </button>
        </div>)
    }
}

//ParentA component
class ParentA extends React.Component { 
    constructor(props) {
        super(props);
    }

    handleToUpdate = (someArg) => {
            alert('We pass argument from Child to Parent: ' + someArg);
    }

    render() {
        return (<div>
            <ChildB handleToUpdate = {this.handleToUpdate} /></div>)
    }
}

if(document.querySelector("#demo")){
    ReactDOM.render(
        <ParentA />,
        document.querySelector("#demo")
    );
}

Schauen Sie sich JSFIDDLE an

römisch
quelle
Dieses ist gut! Könntest du dieses Stück erklären: <ChildB handleToUpdate = {handleToUpdate.bind(this)} />Warum musste ich wieder binden?
Däne
@Dane - Sie müssen den Kontext dafür binden, um das übergeordnete Element zu sein, sodass thissich der Aufruf innerhalb des Kindes thisauf den Status des Elternteils und nicht auf den des Kindes bezieht. Dies ist Schließung vom Feinsten!
Casey
@Casey, aber machen wir das nicht im Konstruktor? Und ist das nicht genug?
Däne
Du hast vollkommen recht! Das habe ich vermisst. Ja, wenn Sie es bereits im Konstruktor getan haben, können Sie loslegen!
Casey
Du bist ein Legendenkamerad! Dadurch bleiben die Komponenten in sich geschlossen, ohne dass übergeordnete Komponenten für den
Statusaustausch erstellt werden müssen
13

Ich möchte mich bei der am besten bewerteten Antwort dafür bedanken, dass sie mir die Idee meines eigenen Problems gegeben hat, im Grunde die Variation davon mit der Pfeilfunktion und der Übergabe von Parametern von der untergeordneten Komponente:

 class Parent extends React.Component {
  constructor(props) {
    super(props)
    // without bind, replaced by arrow func below
  }

  handler = (val) => {
    this.setState({
      someVar: val
    })
  }

  render() {
    return <Child handler = {this.handler} />
  }
}

class Child extends React.Component {
  render() {
    return <Button onClick = {() => this.props.handler('the passing value')}/ >
  }
}

Hoffe es hilft jemandem.

Ardhi
quelle
Was ist das Besondere an der Pfeilfunktion bei Direktanrufen?
Ashish Kamble
@AshishKamble Die thisIn-Pfeil-Funktionen beziehen sich auf den Kontext des Elternteils (dh die ParentKlasse).
CPHPython
Dies ist eine doppelte Antwort. Sie können der akzeptierten Antwort einen Kommentar hinzufügen und diese experimentelle Funktion erwähnen, um die Pfeilfunktion im Unterricht zu verwenden.
Arashsoft
10

Ich mag die Antwort bezüglich der Weitergabe von Funktionen, es ist eine sehr praktische Technik.

Auf der anderen Seite können Sie dies auch mit Pub / Sub oder mit einer Variante, einem Dispatcher, erreichen, wie es Flux tut. Die Theorie ist super einfach: Lassen Sie Komponente 5 eine Nachricht senden, auf die Komponente 3 wartet. Komponente 3 aktualisiert dann ihren Status, wodurch das erneute Rendern ausgelöst wird. Dies erfordert zustandsbehaftete Komponenten, die je nach Sichtweise ein Anti-Pattern sein können oder nicht. Ich bin persönlich gegen sie und möchte lieber, dass etwas anderes von oben nach unten auf Sendungen wartet und den Status ändert (Redux tut dies, fügt aber zusätzliche Terminologie hinzu).

import { Dispatcher } from flux
import { Component } from React

const dispatcher = new Dispatcher()

// Component 3
// Some methods, such as constructor, omitted for brevity
class StatefulParent extends Component {
  state = {
    text: 'foo'
  } 

  componentDidMount() {
    dispatcher.register( dispatch => {
      if ( dispatch.type === 'change' ) {
        this.setState({ text: 'bar' })
      }
    }
  }

  render() {
    return <h1>{ this.state.text }</h1>
  }
}

// Click handler
const onClick = event => {
  dispatcher.dispatch({
    type: 'change'
  })
}

// Component 5 in your example
const StatelessChild = props => {
  return <button onClick={ onClick }>Click me</button> 
}

Das Dispatcher-Bundle mit Flux ist sehr einfach. Es registriert einfach Rückrufe und ruft sie auf, wenn ein Versand erfolgt, und durchläuft den Inhalt des Versands (im obigen knappen Beispiel gibt es keine payloadmit dem Versand, einfach eine Nachrichten-ID). Sie können dies sehr einfach an ein traditionelles Pub / Sub anpassen (z. B. mit dem EventEmitter von Events oder einer anderen Version), wenn dies für Sie sinnvoller ist.

Matt Styles
quelle
Meine Reacts-Komponenten werden im Browser wie in einem offiziellen Tutorial ( facebook.github.io/react/docs/tutorial.html ) "ausgeführt". Ich habe versucht, Flux in browserify aufzunehmen, aber der Browser sagt, dass der Dispatcher nicht gefunden wird :(
wklm
2
Die Syntax, die ich verwendet habe, war die ES2016-Modulsyntax, die eine Transpilation erfordert (ich verwende Babel , aber es gibt andere, die Babelify- Transformation kann mit browserify verwendet werden). Sie transpiliert zuvar Dispatcher = require( 'flux' ).Dispatcher
Matt Styles
8

Ich habe die folgende Arbeitslösung gefunden, um das onClick-Funktionsargument mit param vom untergeordneten Element an die übergeordnete Komponente zu übergeben:

Elternklasse:

class Parent extends React.Component {
constructor(props) {
    super(props)

    // Bind the this context to the handler function
    this.handler = this.handler.bind(this);

    // Set some state
    this.state = {
        messageShown: false
    };
}

// This method will be sent to the child component
handler(param1) {
console.log(param1);
    this.setState({
        messageShown: true
    });
}

// Render the child component and set the action property with the handler as value
render() {
    return <Child action={this.handler} />
}}

Kinderklasse:

class Child extends React.Component {
render() {
    return (
        <div>
            {/* The button will execute the handler function set by the parent component */}
            <Button onClick={this.props.action.bind(this,param1)} />
        </div>
    )
} }
H. parnian
quelle
2
Kann jemand sagen, ob dies eine akzeptable Lösung ist (insbesondere daran interessiert, den Parameter wie vorgeschlagen zu übergeben).
Ilans
param1wird nur auf der Konsole angezeigt, nicht zugewiesen, es wird immer zugewiesentrue
Ashish Kamble
Ich kann nicht mit der Qualität der Lösung sprechen, aber dies überträgt erfolgreich den Parameter für mich.
James
6

Wann immer Sie auf einer beliebigen Ebene zwischen Kind und Elternteil kommunizieren müssen, ist es besser, den Kontext zu nutzen . Definieren Sie in der übergeordneten Komponente den Kontext, der vom untergeordneten Element aufgerufen werden kann, z

In der übergeordneten Komponente in Ihrem Fall Komponente 3

static childContextTypes = {
        parentMethod: React.PropTypes.func.isRequired
      };

       getChildContext() {
        return {
          parentMethod: (parameter_from_child) => this.parentMethod(parameter_from_child)
        };
      }

parentMethod(parameter_from_child){
// update the state with parameter_from_child
}

Teilen Sie dieser Komponente in der untergeordneten Komponente (in Ihrem Fall Komponente 5) einfach mit, dass sie den Kontext ihrer übergeordneten Komponente verwenden möchte.

 static contextTypes = {
       parentMethod: React.PropTypes.func.isRequired
     };
render(){
    return(
      <TouchableHighlight
        onPress={() =>this.context.parentMethod(new_state_value)}
         underlayColor='gray' >   

            <Text> update state in parent component </Text>              

      </TouchableHighlight>
)}

Sie finden das Demo-Projekt bei repo

Rajan Twanabashu
quelle
Ich kann diese Antwort nicht verstehen, können Sie mehr darüber erklären
Ashish Kamble
5

Es scheint, dass wir nur Daten von Eltern zu Kindern weitergeben können, wenn die Reaktion den unidirektionalen Datenfluss fördert. Um jedoch zu veranlassen, dass Eltern sich selbst aktualisieren, wenn etwas in ihrer "untergeordneten Komponente" passiert, verwenden wir im Allgemeinen eine sogenannte "Rückruffunktion".

Wir übergeben die im Elternteil definierte Funktion als "Requisiten" an das Kind und rufen diese Funktion vom Kind auf, das sie in der Elternkomponente auslöst.


class Parent extends React.Component {
  handler = (Value_Passed_From_SubChild) => {
    console.log("Parent got triggered when a grandchild button was clicked");
    console.log("Parent->Child->SubChild");
    console.log(Value_Passed_From_SubChild);
  }
  render() {
    return <Child handler = {this.handler} />
  }
}
class Child extends React.Component {
  render() {
    return <SubChild handler = {this.props.handler}/ >
  }
}
class SubChild extends React.Component { 
  constructor(props){
   super(props);
   this.state = {
      somethingImp : [1,2,3,4]
   }
  }
  render() {
     return <button onClick = {this.props.handler(this.state.somethingImp)}>Clickme<button/>
  }
}
React.render(<Parent />,document.getElementById('app'));

 HTML
 ----
 <div id="app"></div>

In diesem Beispiel können wir Daten von SubChild -> Child -> Parent übergeben, indem wir die Funktion an das direkte Child übergeben.

Vishal Bisht
quelle
4

Ich habe viele Male eine erstklassige Antwort von dieser Seite verwendet, aber während ich React lernte, habe ich einen besseren Weg gefunden, dies zu tun, ohne zu binden und ohne Inline-Funktion in Requisiten.

Schauen Sie einfach hier:

class Parent extends React.Component {

  constructor() {
    super();
    this.state={
      someVar: value
    }
  }

  handleChange=(someValue)=>{
    this.setState({someVar: someValue})
  }

  render() {
    return <Child handler={this.handleChange} />
  }

}

export const Child = ({handler}) => {
  return <Button onClick={handler} />
}

Die Taste befindet sich in einer Pfeilfunktion:

handleChange=(someValue)=>{
  this.setState({someVar: someValue})
}

Sie können mehr lesen hier . Hoffe das wird für jemanden nützlich sein =)

Александр Немков
quelle
Das macht für mich so viel mehr Sinn. Vielen Dank!
Brett Rowberry
3

- Wir können ParentComponent erstellen und mit der handleInputChange-Methode den ParentComponent-Status aktualisieren. Importieren Sie die ChildComponent und wir übergeben zwei Requisiten von der übergeordneten an die untergeordnete Komponente, dh.handleInputChange-Funktion und Anzahl.

import React, { Component } from 'react';
import ChildComponent from './ChildComponent';

class ParentComponent extends Component {
  constructor(props) {
    super(props);
    this.handleInputChange = this.handleInputChange.bind(this);
    this.state = {
      count: '',
    };
  }

  handleInputChange(e) {
    const { value, name } = e.target;
    this.setState({ [name]: value });
  }

  render() {
    const { count } = this.state;
    return (
      <ChildComponent count={count} handleInputChange={this.handleInputChange} />
    );
  }
}
  • Jetzt erstellen wir die ChildComponent-Datei und speichern sie als ChildComponent.jsx. Diese Komponente ist zustandslos, da die untergeordnete Komponente keinen Status hat. Wir verwenden die Prop-Typ-Bibliothek zur Überprüfung des Requisitentyps.

    import React from 'react';
    import { func, number } from 'prop-types';
    
    const ChildComponent = ({ handleInputChange, count }) => (
      <input onChange={handleInputChange} value={count} name="count" />
    );
    
    ChildComponent.propTypes = {
      count: number,
      handleInputChange: func.isRequired,
    };
    
    ChildComponent.defaultProps = {
      count: 0,
    };
    
    export default ChildComponent;
Sachin Metkari
quelle
Wie funktioniert das, wenn das Kind ein Kind hat, das die Requisite seines Elternteils beeinflusst?
Bobort
3

Wenn dasselbe Szenario nicht überall verbreitet ist, können Sie den Kontext von React verwenden, insbesondere wenn Sie nicht den gesamten Overhead einführen möchten, den Statusverwaltungsbibliotheken verursachen. Außerdem ist es einfacher zu lernen. Aber seien Sie vorsichtig, Sie könnten es überbeanspruchen und anfangen, schlechten Code zu schreiben. Grundsätzlich definieren Sie eine Containerkomponente (die diesen Status für Sie speichert und beibehält), sodass alle Komponenten, die daran interessiert sind, dieses Datenelement zu schreiben / lesen, ihre untergeordneten Elemente sind (nicht unbedingt direkte untergeordnete Elemente).

https://reactjs.org/docs/context.html

Sie können stattdessen auch einfach React richtig verwenden.

<Component5 onSomethingHappenedIn5={this.props.doSomethingAbout5} />

Übergeben Sie doSomethingAbout5 an Komponente 1

    <Component1>
        <Component2 onSomethingHappenedIn5={somethingAbout5 => this.setState({somethingAbout5})}/>
        <Component5 propThatDependsOn5={this.state.somethingAbout5}/>
    <Component1/>

Wenn dies ein häufiges Problem ist, sollten Sie darüber nachdenken, den gesamten Status der Anwendung an einen anderen Ort zu verschieben. Sie haben einige Optionen, die häufigsten sind:

https://redux.js.org/

https://facebook.github.io/flux/

Anstatt den Anwendungsstatus in Ihrer Komponente zu verwalten, senden Sie grundsätzlich Befehle, wenn etwas passiert, um den Status zu aktualisieren. Komponenten ziehen den Status auch aus diesem Container, sodass alle Daten zentralisiert werden. Dies bedeutet nicht, dass der lokale Status nicht mehr verwendet werden kann, aber das ist ein fortgeschritteneres Thema.

Pato Loco
quelle
2

Wenn Sie also die übergeordnete Komponente aktualisieren möchten,

 class ParentComponent extends React.Component {
        constructor(props){
            super(props);
            this.state = {
               page:0
            }
        }

        handler(val){
            console.log(val) // 1
        }

        render(){
          return (
              <ChildComponent onChange={this.handler} />
           )
       }
   }


class ChildComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
             page:1
        };
    }

    someMethod = (page) => {
        this.setState({ page: page });
        this.props.onChange(page)
    }

    render() {
        return (
       <Button
            onClick={() => this.someMethod()} 
       > Click
        </Button>
      )
   }
}

Hier ist onChange ein Attribut, dessen "Handler" -Methode an seine Instanz gebunden ist. Wir haben den Methodenhandler an die Child-Klassenkomponente übergeben, um ihn über die onChange-Eigenschaft in ihrem Requisitenargument zu empfangen.

Das Attribut onChange wird in einem Requisitenobjekt wie folgt festgelegt:

props ={
onChange : this.handler
}

und an die untergeordnete Komponente übergeben

So kann die untergeordnete Komponente wie folgt auf den Wert des Namens im Requisitenobjekt zugreifen

Dies geschieht durch die Verwendung von Render-Requisiten.

Jetzt verfügt die Child-Komponente über eine Schaltfläche "Click" mit einem Onclick-Ereignis, mit dem die über onChnge in ihrem Requisitenargumentobjekt an sie übergebene Handlermethode aufgerufen werden kann. Jetzt enthält this.props.onChange in Child die Ausgabemethode in der übergeordneten Klassenreferenz und Credits: Bits and Pieces

Preetham NT
quelle
Entschuldigung .. für die Verzögerung, hier ist onChange ein Attribut mit der "Handler" -Methode, die an die Instanz gebunden ist. Wir haben den Methodenhandler an die Child-Klassenkomponente übergeben, um ihn über die onChange-Eigenschaft in ihrem Requisitenargument zu empfangen. Das Attribut onChange wird in einem Requisitenobjekt wie folgt festgelegt: props = {onChange: this.handler} und an die untergeordnete Komponente übergeben. Die untergeordnete Komponente kann also wie folgt auf den Wert des Namens im Requisitenobjekt zugreifen die Verwendung von Render Requisiten. Referenz und Credits: [ blog.bitsrc.io/…
Preetham NT
0

So mache ich es.

type ParentProps = {}
type ParentState = { someValue: number }
class Parent extends React.Component<ParentProps, ParentState> {
    constructor(props: ParentProps) {
        super(props)
        this.state = { someValue: 0 }

        this.handleChange = this.handleChange.bind(this)
    }

    handleChange(value: number) {
        this.setState({...this.state, someValue: value})
    }

    render() {
        return <div>
            <Child changeFunction={this.handleChange} defaultValue={this.state.someValue} />
            <p>Value: {this.state.someValue}</p>
        </div>
    }
}

type ChildProps = { defaultValue: number, changeFunction: (value: number) => void}
type ChildState = { anotherValue: number }
class Child extends React.Component<ChildProps, ChildState> {
    constructor(props: ChildProps) {
        super(props)
        this.state = { anotherValue: this.props.defaultValue }

        this.handleChange = this.handleChange.bind(this)
    }

    handleChange(value: number) {
        this.setState({...this.state, anotherValue: value})
        this.props.changeFunction(value)
    }

    render() {
        return <div>
            <input onChange={event => this.handleChange(Number(event.target.value))} type='number' value={this.state.anotherValue}/>
        </div>
    }
}
ICH BIN DER BESTE
quelle
0

Die meisten der oben angegebenen Antworten beziehen sich auf React.Component-basierte Designs. Wenn Sie useStatein den letzten Upgrades der React-Bibliothek verwenden, befolgen Sie diese Antwort

leeCoder
quelle
-3
<Footer 
  action={()=>this.setState({showChart: true})}
/>

<footer className="row">
    <button type="button" onClick={this.props.action}>Edit</button>
  {console.log(this.props)}
</footer>

Try this example to write inline setState, it avoids creating another function.
suresh
quelle
Es verursacht Leistungsprobleme: stackoverflow.com/q/36677733/3328979
Arashsoft