Rufen Sie die untergeordnete Methode vom übergeordneten Element auf

474

Ich habe zwei Komponenten.

  1. Übergeordnete Komponente
  2. Untergeordnete Komponente

Ich habe versucht, die Methode des Kindes von Parent aufzurufen. Ich habe es auf diese Weise versucht, konnte aber kein Ergebnis erzielen

class Parent extends Component {
  render() {
    return (
      <Child>
        <button onClick={Child.getAlert()}>Click</button>
      </Child>
      );
    }
  }

class Child extends Component {
  getAlert() {
    alert('clicked');
  }

  render() {
    return (
      <h1 ref="hello">Hello</h1>
    );
  }
}

Gibt es eine Möglichkeit, die Methode des Kindes vom Elternteil aufzurufen?

Hinweis: Untergeordnete und übergeordnete Komponenten befinden sich in zwei verschiedenen Dateien

N8FURY
quelle
Obwohl ich sehr spät dran bin, lerne ich auch Reagieren, sodass ich gerne wissen möchte, in welchem ​​Fall ein Elternteil die Kindermethode aufrufen müsste. Würden Sie bitte erklären?
Akshay Raut
Weiß jemand wie man das aus Pfeilfunktionen macht? stackoverflow.com/questions/60015693/…
Thomas Segato
@AkshayRaut IMO ein guter Anwendungsfall: Ein Allzweckformular mit Funktionen zum Zurücksetzen und Senden, von denen das spätere die Formularwerte zurückgibt.
Julian K

Antworten:

702

Lassen Sie mich zunächst ausdrücken, dass dies im Allgemeinen nicht der richtige Weg ist, um Dinge im React-Land zu erledigen. Normalerweise möchten Sie die Funktionalität an Kinder in Requisiten weitergeben und Benachrichtigungen von Kindern in Ereignissen weitergeben (oder noch besser :) dispatch.

Wenn Sie jedoch eine zwingende Methode für eine untergeordnete Komponente verfügbar machen müssen , können Sie refs verwenden . Denken Sie daran, dass dies eine Notluke ist und normalerweise anzeigt, dass ein besseres Design verfügbar ist.

Bisher wurden Refs nur für klassenbasierte Komponenten unterstützt. Mit dem Aufkommen von React Hooks ist dies nicht mehr der Fall

Verwenden von Hooks und Funktionskomponenten ( >= [email protected])

const { forwardRef, useRef, useImperativeHandle } = React;

// We need to wrap component in `forwardRef` in order to gain
// access to the ref object that is assigned using the `ref` prop.
// This ref is passed as the second parameter to the function component.
const Child = forwardRef((props, ref) => {

  // The component instance will be extended
  // with whatever you return from the callback passed
  // as the second argument
  useImperativeHandle(ref, () => ({

    getAlert() {
      alert("getAlert from Child");
    }

  }));

  return <h1>Hi</h1>;
});

const Parent = () => {
  // In order to gain access to the child component instance,
  // you need to assign it to a `ref`, so we call `useRef()` to get one
  const childRef = useRef();

  return (
    <div>
      <Child ref={childRef} />
      <button onClick={() => childRef.current.getAlert()}>Click</button>
    </div>
  );
};

ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>

<div id="root"></div>

Dokumentation für useImperativeHandle()ist hier :

useImperativeHandlePasst den Instanzwert an, der bei Verwendung für übergeordnete Komponenten verfügbar gemacht wird ref.

Verwenden von Klassenkomponenten ( >= [email protected])

const { Component } = React;

class Parent extends Component {
  constructor(props) {
    super(props);
    this.child = React.createRef();
  }

  onClick = () => {
    this.child.current.getAlert();
  };

  render() {
    return (
      <div>
        <Child ref={this.child} />
        <button onClick={this.onClick}>Click</button>
      </div>
    );
  }
}

class Child extends Component {
  getAlert() {
    alert('getAlert from Child');
  }

  render() {
    return <h1>Hello</h1>;
  }
}

ReactDOM.render(<Parent />, document.getElementById('root'));
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>

Legacy API ( <= [email protected])

Aus historischen Gründen ist hier der auf Rückrufen basierende Stil, den Sie mit React-Versionen vor 16.3 verwenden würden:

const { Component } = React;
const { render } = ReactDOM;

class Parent extends Component {
  render() {
    return (
      <div>
        <Child ref={instance => { this.child = instance; }} />
        <button onClick={() => { this.child.getAlert(); }}>Click</button>
      </div>
    );
  }
}

class Child extends Component {
  getAlert() {
    alert('clicked');
  }

  render() {
    return (
      <h1>Hello</h1>
    );
  }
}


render(
  <Parent />,
  document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

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

rossipedia
quelle
23
Ich bin müde, aber am Ende habe ich den Fehler "_this2.refs.child.getAlert ist keine Funktion"
N8FURY
22
Dies liegt daran, dass connecteine Komponente höherer Ordnung zurückgegeben wird, die Ihre ursprüngliche Instanz umschließt. Sie müssen getWrappedInstance()zuerst die angeschlossene Komponente aufrufen , um Ihre ursprüngliche Komponente zu erhalten. Dann können Sie Instanzmethoden dafür aufrufen.
Wikipedia
16
Dies ist kein wirklich gutes Muster. Ganz zu schweigen davon, dass String Refs verpönt sind. Es ist besser, Requisiten an die untergeordnete Komponente zu übergeben und dann durch Klicken auf eine Schaltfläche im übergeordneten Element den Status des übergeordneten Elements zu ändern und dem untergeordneten Element ein Statuselement zu übergeben, das das untergeordnete Element auslöst componentWillReceiveProps, und dieses als Auslöser zu verwenden.
ffxsam
7
Nein, es ist normalerweise nicht das beste Muster, es ist eher eine Notluke, wenn Sie es brauchen, und sollte nur in Notfällen verwendet werden. Diese Antwort wurde auch geschrieben, als es noch String-Refs gab, und Sie haben Recht, dass sie heutzutage nicht die "richtige" Art sind, Dinge zu tun.
Wikipedia
35
Wenn die beste Vorgehensweise darin besteht, ein Labyrinth aus Logik zu erstellen, um etwas so Einfaches wie das Aufrufen der Methode einer untergeordneten Komponente zu tun, stimme ich der bewährten Vorgehensweise nicht zu.
aaaaaa
149

Sie können hier ein anderes Muster verwenden:

class Parent extends Component {
 render() {
  return (
    <div>
      <Child setClick={click => this.clickChild = click}/>
      <button onClick={() => this.clickChild()}>Click</button>
    </div>
  );
 }
}

class Child extends Component {
 constructor(props) {
    super(props);
    this.getAlert = this.getAlert.bind(this);
 }
 componentDidMount() {
    this.props.setClick(this.getAlert);
 }
 getAlert() {
    alert('clicked');
 }
 render() {
  return (
    <h1 ref="hello">Hello</h1>
  );
 }
}

Sie legen die clickChildMethode des Elternteils fest, wenn das Kind bereitgestellt wird. Auf diese Weise wird beim Klicken auf die Schaltfläche im übergeordneten Element aufgerufen, clickChildwelches das untergeordnete Element anruft getAlert.

Dies funktioniert auch, wenn Ihr Kind mit eingewickelt ist, connect()sodass Sie den getWrappedInstance()Hack nicht benötigen .

Beachten Sie, dass Sie nicht onClick={this.clickChild}in Eltern verwenden können, da beim Rendern von Eltern Kinder nicht gemountet sind und daher this.clickChildnoch nicht zugewiesen sind. Die Verwendung onClick={() => this.clickChild()}ist in Ordnung, da beim Klicken die Schaltfläche this.clickChildbereits zugewiesen sein sollte.

zugemauert
quelle
5
Ich _this2.clickChild is not a functionverstehe warum?
Tatsu
1
Vergiss nicht, dass dies für mich funktioniert hat: github.com/kriasoft/react-starter-kit/issues/…
tatsu
5
beides hat nicht funktioniert. Nur diese Antwort funktionierte: github.com/kriasoft/react-starter-kit/issues/…
tatsu
2
Dies ist eine interessante Technik. Es ist ziemlich sauber und scheint keine Regeln zu brechen. Aber ich denke, Ihre Antwort wäre vollständiger (und würde die Erwartungen erfüllen), wenn Sie eine Bindung hinzufügen würden. Die Antwort hat mir so gut gefallen, dass ich sie zu diesem verwandten Github-Problem gepostet habe .
Joeytwiddle
7
Dies sollte die akzeptierte Antwort sein
Jesus Gomez
27

https://facebook.github.io/react/tips/expose-component-functions.html Weitere Antworten finden Sie hier. Rufen Sie Methoden zum Reagieren untergeordneter Komponenten auf

Wenn Sie sich die Refs der "Grund" -Komponente ansehen, brechen Sie die Kapselung und machen es unmöglich, diese Komponente zu refaktorieren, ohne alle verwendeten Stellen sorgfältig zu untersuchen. Aus diesem Grund empfehlen wir dringend, Refs als privat für eine Komponente zu behandeln, ähnlich wie bei state.

Im Allgemeinen sollten Daten über Requisiten an den Baum weitergegeben werden. Es gibt einige Ausnahmen (z. B. das Aufrufen von .focus () oder das Auslösen einer einmaligen Animation, die den Status nicht wirklich "ändert"), aber jedes Mal, wenn Sie eine Methode namens "set" verfügbar machen, sind dies normalerweise Requisiten eine bessere Wahl. Versuchen Sie, es so zu gestalten, dass sich die innere Eingabekomponente um ihre Größe und ihr Aussehen kümmert, damit keiner ihrer Vorfahren dies tut.

Mike Tronic
quelle
5
Hier ist die Quelle dieser Antwort: Diskussion.reactjs.org/t/… . Keine Probleme mit dem Zitieren anderer, aber zumindest einige Referenzen.
Jodo
1
Wie genau bricht diese Kapselung mehr als Requisiten?
Timmmm
16

Alternative Methode mit useEffect:

Elternteil:

const [refresh, doRefresh] = useState(0);
<Button onClick={()=>doRefresh(refresh+1)} />
<Children refresh={refresh} />

Kinder:

useEffect(() => {
    refresh(); //children function of interest
  }, [props.refresh]);
tonymayoral
quelle
2
Dies sollte mehr Stimmen bekommen
Ali Al Amine
Ps. Wenn Sie das Formular nur erneut rendern möchten (z. B. um die Eingabefelder zurückzusetzen), müssen Sie nicht einmal den useEffect einschließen. Sie können einfach die Requisite in die Komponentenänderung einfügen
Matt Fletcher,
8

Wir können refs auch anders verwenden

Wir werden ein übergeordnetes Element erstellen, es wird eine <Child/>Komponente rendern . Wie Sie sehen können, müssen Sie für die zu rendernde Komponente das ref- Attribut hinzufügen und einen Namen dafür angeben.
Dann triggerChildAlertgreift die in der übergeordneten Klasse befindliche Funktion auf die refs-Eigenschaft dieses Kontexts zu (wenn die triggerChildAlertFunktion ausgelöst wird, greift sie auf die untergeordnete Referenz zu und verfügt über alle Funktionen des untergeordneten Elements).

class Parent extends React.Component {
    triggerChildAlert(){
        this.refs.child.callChildMethod();
        // to get child parent returned  value-
        // this.value = this.refs.child.callChildMethod();
        // alert('Returned value- '+this.value);
    }

    render() {
        return (
            <div>
                {/* Note that you need to give a value to the ref parameter, in this case child*/}
                <Child ref="child" />
                <button onClick={this.triggerChildAlert}>Click</button>
            </div>
        );
    }
}  

Nun sieht die untergeordnete Komponente, wie sie zuvor theoretisch entworfen wurde, folgendermaßen aus:

class Child extends React.Component {
    callChildMethod() {
        alert('Hello World');
        // to return some value
        // return this.state.someValue;
    }

    render() {
        return (
            <h1>Hello</h1>
        );
    }
}

Hier ist der Quellcode -
Hope wird Ihnen helfen!

S. Yadav
quelle
1
String-Refs sind veraltet. reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs
Cory McAboy
4

Wenn Sie dies einfach tun, weil Sie möchten, dass das Kind seinen Eltern ein wiederverwendbares Merkmal zur Verfügung stellt, können Sie dies stattdessen mit Render-Requisiten tun .

Diese Technik stellt die Struktur tatsächlich auf den Kopf. Das Childschließt jetzt das Elternteil ein, also habe ich es AlertTraitunten umbenannt. Ich habe den Namen Parentfür Kontinuität beibehalten , obwohl es jetzt nicht wirklich ein Elternteil ist.

// Use it like this:

  <AlertTrait renderComponent={Parent}/>


class AlertTrait extends Component {
  // You may need to bind this function, if it is stateful
  doAlert() {
    alert('clicked');
  }
  render() {
    return this.props.renderComponent(this.doAlert);
  }
}

class Parent extends Component {
  render() {
    return (
      <button onClick={this.props.doAlert}>Click</button>
    );
  }
}

In diesem Fall bietet das AlertTrait eine oder mehrere Eigenschaften, die es als Requisiten an die Komponente weitergibt, die es in seiner renderComponentRequisite erhalten hat.

Der Elternteil erhält doAlertals Requisite und kann ihn bei Bedarf anrufen.

(Aus Gründen der Übersichtlichkeit habe ich die Requisite renderComponentim obigen Beispiel aufgerufen. In den oben verlinkten React-Dokumenten wird sie jedoch nur aufgerufen render.)

Die Trait-Komponente kann in ihrer Renderfunktion Dinge um das übergeordnete Element rendern, aber sie rendert nichts innerhalb des übergeordneten Elements. Tatsächlich könnte es Dinge innerhalb des renderChildübergeordneten Elements rendern, wenn es dem übergeordneten Element eine andere Requisite (z. B. ) übergibt, die das übergeordnete Element dann während seiner Rendermethode verwenden könnte.

Dies unterscheidet sich etwas von dem, was das OP verlangt hat, aber einige Leute könnten hier landen (wie wir), weil sie ein wiederverwendbares Merkmal erstellen wollten und dachten, dass eine untergeordnete Komponente ein guter Weg ist, dies zu tun.

joeytwiddle
quelle
Es gibt eine praktische Liste von Mustern zum Erstellen wiederverwendbarer Merkmale hier: reactjs.org/blog/2016/07/13/…
joeytwiddle
Was ist, wenn Sie N Stoppuhren und eine Taste haben, um sie alle neu zu starten? Wie sind Requisiten hier praktisch?
vsync
@vsync Ich bin nicht sicher, ob diese Methode für Ihre Aufgabe helfen kann. Aber die Antwort von Brickingup könnte helfen. Beachten Sie, dass sie eingestellt sind, this.clickChild = clickaber Ihre mehreren Stoppuhren mehrere Funktionen übergeben würden, sodass Sie alle speichern müssten:this.watchRestartFuncs[watchId] = restartWatch
joeytwiddle
1

Sie können dies auf diese Weise leicht erreichen

Schritte-

  1. Erstellen Sie eine boolesche Variable im Status der übergeordneten Klasse. Aktualisieren Sie dies, wenn Sie eine Funktion aufrufen möchten.
  2. Erstellen Sie eine Requisitenvariable und weisen Sie die boolesche Variable zu.
  3. Greifen Sie von der untergeordneten Komponente mit Requisiten auf diese Variable zu und führen Sie die gewünschte Methode aus, indem Sie eine if-Bedingung haben.

    class Child extends Component {
       Method=()=>{
       --Your method body--
       }
       render() {
         return (
        //check whether the variable has been updated or not
          if(this.props.updateMethod){
            this.Method();
          }
         )
       }
    }
    
    class Parent extends Component {
    
    constructor(){
      this.state={
       callMethod:false
      }
    
    }
    render() {
       return (
    
         //update state according to your requirement
         this.setState({
            callMethod:true
         }}
         <Child updateMethod={this.state.callMethod}></Child>
        );
       }
    }
Kusal Kithmal
quelle
Vielleicht möchten Sie dies sandboxen. Es sieht so aus, als würden Sie eine Endlosschleife erhalten, da die untergeordnete Methode kontinuierlich ausgeführt wird, weil der übergeordnete Status auf true gesetzt ist.
Isaac Pak
@IsaacPak Ja, deshalb habe ich dort einen Kommentar hinterlassen, der besagt, dass Sie den Status gemäß Ihren Anforderungen aktualisieren müssen. Dann läuft es nicht als Endlosschleife.
Kusal Kithmal
1

Ich benutze useEffectHook, um die Kopfschmerzen zu überwinden, die damit verbunden sind. Jetzt übergebe ich eine Variable wie folgt an ein Kind:

<ParentComponent>
 <ChildComponent arbitrary={value} />
</ParentComponent>
useEffect(() => callTheFunctionToBeCalled(value) , [value]);
Schwarze Mamba
quelle
1

Ich war mit keiner der hier vorgestellten Lösungen zufrieden. Es gibt tatsächlich eine sehr einfache Lösung, die mit reinem Javascript durchgeführt werden kann, ohne auf andere React-Funktionen als das grundlegende Requisitenobjekt angewiesen zu sein - und die Ihnen den Vorteil bietet, in beide Richtungen zu kommunizieren (Eltern -> Kind, Kind -> Eltern). Sie müssen ein Objekt von der übergeordneten Komponente an die untergeordnete Komponente übergeben. Dieses Objekt ist das, was ich als "bidirektionale Referenz" oder kurz biRef bezeichne. Grundsätzlich enthält das Objekt einen Verweis auf Methoden im übergeordneten Objekt, die das übergeordnete Objekt verfügbar machen möchte. Die untergeordnete Komponente hängt Methoden an das Objekt an, das die übergeordnete Komponente aufrufen kann. Etwas wie das:

// Parent component.
function MyParentComponent(props) {

   function someParentFunction() {
      // The child component can call this function.
   }

   function onButtonClick() {
       // Call the function inside the child component.
       biRef.someChildFunction();
   }

   // Add all the functions here that the child can call.
   var biRef = {
      someParentFunction: someParentFunction
   }

   return <div>
       <MyChildComponent biRef={biRef} />
       <Button onClick={onButtonClick} />
   </div>;
}


// Child component
function MyChildComponent(props) {

   function someChildFunction() {
      // The parent component can call this function.
   }


   function onButtonClick() {
      // Call the parent function.
      props.biRef.someParentFunction();
   }

   // Add all the child functions to props.biRef that you want the parent
   // to be able to call.
   props.biRef.someChildFunction = someChildFunction;

   return <div>
       <Button onClick={onButtonClick} />
   </div>;
}

Der andere Vorteil dieser Lösung besteht darin, dass Sie dem übergeordneten und dem untergeordneten Element viel mehr Funktionen hinzufügen können, während Sie diese mit nur einer einzigen Eigenschaft vom übergeordneten Element an das untergeordnete Element übergeben.

Eine Verbesserung gegenüber dem obigen Code besteht darin, die übergeordneten und untergeordneten Funktionen nicht direkt zum biRef-Objekt, sondern zu Untermitgliedern hinzuzufügen. Übergeordnete Funktionen sollten einem Mitglied namens "parent" hinzugefügt werden, während die untergeordneten Funktionen einem Mitglied namens "child" hinzugefügt werden sollten.

// Parent component.
function MyParentComponent(props) {

   function someParentFunction() {
      // The child component can call this function.
   }

   function onButtonClick() {
       // Call the function inside the child component.
       biRef.child.someChildFunction();
   }

   // Add all the functions here that the child can call.
   var biRef = {
      parent: {
          someParentFunction: someParentFunction
      }
   }

   return <div>
       <MyChildComponent biRef={biRef} />
       <Button onClick={onButtonClick} />
   </div>;
}


// Child component
function MyChildComponent(props) {

   function someChildFunction() {
      // The parent component can call this function.
   }


   function onButtonClick() {
      // Call the parent function.
      props.biRef.parent.someParentFunction();
   }

   // Add all the child functions to props.biRef that you want the parent
   // to be able to call.
   props.biRef {
       child: {
            someChildFunction: someChildFunction
       }
   }

   return <div>
       <Button onClick={onButtonClick} />
   </div>;
}

Wenn Sie übergeordnete und untergeordnete Funktionen in separate Elemente des biRef-Objekts einfügen, haben Sie eine saubere Trennung zwischen den beiden und können leicht erkennen, welche zu Eltern oder untergeordneten Elementen gehören. Es hilft auch zu verhindern, dass eine untergeordnete Komponente versehentlich eine übergeordnete Funktion überschreibt, wenn in beiden dieselbe Funktion angezeigt wird.

Eine letzte Sache ist, dass, wenn Sie bemerken, die übergeordnete Komponente das biRef-Objekt mit var erstellt, während die untergeordnete Komponente über das Requisitenobjekt darauf zugreift. Es könnte verlockend sein, das biRef-Objekt im übergeordneten Objekt nicht zu definieren und von seinem übergeordneten Objekt über seinen eigenen Requisitenparameter darauf zuzugreifen (was in einer Hierarchie von UI-Elementen der Fall sein kann). Dies ist riskant, da das Kind möglicherweise denkt, dass eine Funktion, die es dem Elternteil aufruft, dem Elternteil gehört, wenn es tatsächlich einem Großelternteil gehört. Daran ist nichts auszusetzen, solange Sie sich dessen bewusst sind. Sofern Sie keinen Grund haben, eine Hierarchie über eine Eltern-Kind-Beziehung hinaus zu unterstützen, ist es am besten, das biRef in Ihrer übergeordneten Komponente zu erstellen.

AndroidDev
quelle
0

Ich denke, dass der einfachste Weg, Methoden aufzurufen, darin besteht, eine Anforderung für die untergeordnete Komponente festzulegen. Sobald das Kind die Anforderung bearbeitet, ruft es eine Rückrufmethode auf, um die Anforderung zurückzusetzen.

Der Rücksetzmechanismus ist erforderlich, um dieselbe Anforderung mehrmals nacheinander senden zu können.

In der übergeordneten Komponente

In der Rendermethode des übergeordneten Elements:

const { request } = this.state;
return (<Child request={request} onRequestHandled={()->resetRequest()}/>);

Der Elternteil benötigt zwei Methoden, um mit seinem Kind in zwei Richtungen zu kommunizieren.

sendRequest() {
  const request = { param: "value" };
  this.setState({ request });
}

resetRequest() {
  const request = null;
  this.setState({ request });
}

In der untergeordneten Komponente

Das Kind aktualisiert seinen internen Status und kopiert die Anforderung von den Requisiten.

constructor(props) {
  super(props);
  const { request } = props;
  this.state = { request };
}

static getDerivedStateFromProps(props, state) {
  const { request } = props;
  if (request !== state.request ) return { request };
  return null;
}

Anschließend wird die Anforderung verarbeitet und der Reset an das übergeordnete Element gesendet:

componentDidMount() {
  const { request } = this.state;
  // todo handle request.

  const { onRequestHandled } = this.props;
  if (onRequestHandled != null) onRequestHandled();
}
bvdb
quelle
0

Eine andere Möglichkeit, eine untergeordnete Funktion vom übergeordneten Element aus auszulösen, besteht darin, die componentDidUpdateFunktion in der untergeordneten Komponente zu verwenden. Ich gebe eine Requisite triggerChildFuncvon Eltern an Kind weiter, was anfangs so ist null. Der Wert ändert sich in eine Funktion, wenn auf die Schaltfläche geklickt wird und Child dies ändertcomponentDidUpdate und seine eigene interne Funktion aufruft.

Da prop triggerChildFunczu einer Funktion wechselt, erhalten wir auch einen Rückruf an das übergeordnete Element. Wenn Parent nicht wissen muss, wann die Funktion aufgerufen wird, kann sich der Wert triggerChildFuncbeispielsweise von nullzu ändern true.

const { Component } = React;
const { render } = ReactDOM;

class Parent extends Component {
  state = {
    triggerFunc: null
  }

  render() {
    return (
      <div>
        <Child triggerChildFunc={this.state.triggerFunc} />
        <button onClick={() => {
          this.setState({ triggerFunc: () => alert('Callback in parent')})
        }}>Click
        </button>
      </div>
    );
  }
}

class Child extends Component {
  componentDidUpdate(prevProps) {
    if (this.props.triggerChildFunc !== prevProps.triggerChildFunc) {
      this.onParentTrigger();
    }
  }

  onParentTrigger() {
    alert('parent triggered me');

    // Let's call the passed variable from parent if it's a function
    if (this.props.triggerChildFunc && {}.toString.call(this.props.triggerChildFunc) === '[object Function]') {
      this.props.triggerChildFunc();
    }
  }

  render() {
    return (
      <h1>Hello</h1>
    );
  }
}


render(
  <Parent />,
  document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='app'></div>

CodePen: https://codepen.io/calsal/pen/NWPxbJv?editors=1010

Calsal
quelle
0

Hier meine Demo: https://stackblitz.com/edit/react-dgz1ee?file=styles.css

Ich verwende useEffect, um die Methoden der untergeordneten Komponente aufzurufen. Ich habe es mit versucht, Proxy and Setter_Getteraber sor useEffectscheint die bequemere Möglichkeit zu sein, eine untergeordnete Methode vom Elternteil aufzurufen. Um Proxy and Setter_Getteres zu verwenden , scheint es eine gewisse Subtilität zu geben, die zuerst überwunden werden muss, da das zuerst gerenderte Element durch die Spezifität des Objekts ein objektähnliches Element ist ref.current return => <div/>. ÜberuseEffect auf diesen Ansatz können Sie auch den Status der Eltern festlegen, je nachdem, was Sie mit den Kindern tun möchten.

In dem von mir bereitgestellten Link der Demo finden Sie meinen vollständigen ReactJS-Code mit meinen Entwürfen, damit Sie den Workflow meiner Lösung schätzen können.

Hier versorge ich Sie nur mit dem entsprechenden Code für das ReactJS-Snippet. ::

import React, {
  Component,
  createRef,
  forwardRef,
  useState,
  useEffect
} from "react"; 

{...}

// Child component
// I am defining here a forwardRef's element to get the Child's methods from the parent
// through the ref's element.
let Child = forwardRef((props, ref) => {
  // I am fetching the parent's method here
  // that allows me to connect the parent and the child's components
  let { validateChildren } = props;
  // I am initializing the state of the children
  // good if we can even leverage on the functional children's state
  let initialState = {
    one: "hello world",
    two: () => {
      console.log("I am accessing child method from parent :].");
      return "child method achieve";
    }
  };
  // useState initialization
  const [componentState, setComponentState] = useState(initialState);
  // useEffect will allow me to communicate with the parent
  // through a lifecycle data flow
  useEffect(() => {
    ref.current = { componentState };
    validateChildren(ref.current.componentState.two);
  });

{...}

});

{...}

// Parent component
class App extends Component {
  // initialize the ref inside the constructor element
  constructor(props) {
    super(props);
    this.childRef = createRef();
  }

  // I am implementing a parent's method
  // in child useEffect's method
  validateChildren = childrenMethod => {
    // access children method from parent
    childrenMethod();
    // or signaling children is ready
    console.log("children active");
  };

{...}
render(){
       return (
          {
            // I am referencing the children
            // also I am implementing the parent logic connector's function
            // in the child, here => this.validateChildren's function
          }
          <Child ref={this.childRef} validateChildren={this.validateChildren} />
        </div>
       )
}
Webfrau
quelle
0

Wir freuen uns über einen benutzerdefinierten Haken, den wir anrufen useCounterKey. Es wird nur ein CounterKey oder ein Schlüssel eingerichtet, der von Null an zählt. Die zurückgegebene Funktion setzt die Taste zurück (dh inkrementieren). (Ich glaube, dies ist der idiomatischste Weg in React , um eine Komponente zurückzusetzen - drücken Sie einfach die Taste.)

Dieser Hook funktioniert jedoch auch in allen Situationen, in denen Sie eine einmalige Nachricht an den Client senden möchten, um etwas zu tun. Zum Beispiel verwenden wir es, um ein Steuerelement im Kind auf ein bestimmtes übergeordnetes Ereignis zu fokussieren - es fokussiert nur automatisch, wenn der Schlüssel aktualisiert wird. (Wenn mehr Requisiten benötigt werden, können diese vor dem Zurücksetzen des Schlüssels festgelegt werden, damit sie verfügbar sind, wenn das Ereignis eintritt.)

Diese Methode hat eine gewisse Lernkurve, da sie nicht so einfach ist wie ein typischer Ereignishandler, aber es scheint die idiomatischste Art zu sein, dies in React zu handhaben, die wir gefunden haben (da Tasten bereits auf diese Weise funktionieren). Auf jeden Fall offen für Feedback zu dieser Methode, aber es funktioniert gut!

// Main helper hook:
export function useCounterKey() {
  const [key, setKey] = useState(0);
  return [key, () => setKey(prev => prev + 1)] as const;
}

Beispielverwendungen:

// Sample 1 - normal React, just reset a control by changing Key on demand
function Sample1() {
  const [inputLineCounterKey, resetInputLine] = useCounterKey();

  return <>
    <InputLine key={inputLineCounterKey} />
    <button onClick={() => resetInputLine()} />
  <>;
}

// Second sample - anytime the counterKey is incremented, child calls focus() on the input
function Sample2() {
  const [amountFocusCounterKey, focusAmountInput] = useCounterKey();

  // ... call focusAmountInput in some hook or event handler as needed

  return <WorkoutAmountInput focusCounterKey={amountFocusCounterKey} />
}

function WorkoutAmountInput(props) {
  useEffect(() => {
    if (counterKey > 0) {
      // Don't focus initially
      focusAmount();
    }
  }, [counterKey]);

  // ...
}

( Dank an Kent Dodds für das counterKey-Konzept .)

Freewalker
quelle
-1

Hier ist ein Fehler? zu beachten: Ich stimme der Lösung von rossipedia mit forwardRef, useRef, useImperativeHandle zu

Es gibt einige Fehlinformationen online, die besagen, dass Refs nur aus React Class-Komponenten erstellt werden können. Sie können jedoch tatsächlich Funktionskomponenten verwenden, wenn Sie die oben genannten Hooks verwenden. Ein Hinweis, die Hooks funktionierten nur für mich, nachdem ich die Datei so geändert hatte, dass sie beim Exportieren der Komponente nicht mit Router () verwendet wurde. Dh eine Abwechslung von

export default withRouter(TableConfig);

stattdessen sein

export default TableConfig;

Im Nachhinein wird withRouter () für eine solche Komponente ohnehin nicht benötigt, aber normalerweise schadet es nichts, wenn es vorhanden ist. Mein Anwendungsfall ist, dass ich eine Komponente erstellt habe, um eine Tabelle zum Anzeigen und Bearbeiten von Konfigurationswerten zu erstellen. und ich wollte in der Lage sein, dieser untergeordneten Komponente anzuweisen, ihre Statuswerte zurückzusetzen, wenn die Schaltfläche "Zurücksetzen" des übergeordneten Formulars gedrückt wurde. UseRef () würde ref oder ref.current (immer null) nicht richtig abrufen, bis ich withRouter () aus der Datei entfernt habe, die meine untergeordnete Komponente TableConfig enthält

DeltaPng
quelle