Wywołaj metodę potomną od rodzica

473

Mam dwa elementy.

  1. Komponent macierzysty
  2. Komponent potomny

Próbowałem wywołać metodę dziecka od rodzica, próbowałem w ten sposób, ale nie mogłem uzyskać rezultatu

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>
    );
  }
}

Czy istnieje sposób na wywołanie metody dziecka od rodzica?

Uwaga: komponenty podrzędne i nadrzędne znajdują się w dwóch różnych plikach

N8FURY
źródło
Chociaż jestem bardzo spóźniony, uczę się również React, więc chciałbym wiedzieć o przypadku, w którym rodzic musiałby wywołać metodę potomną. Czy mógłbyś wyjaśnić?
Akshay Raut
Czy ktoś wie jak to zrobić z funkcji strzałek? stackoverflow.com/questions/60015693/…
Thomas Segato
@AkshayRaut IMO dobry przypadek użycia: formularz ogólnego przeznaczenia z funkcjami resetowania i przesyłania, który później zwraca wartości formularza.
Julian K

Odpowiedzi:

702

Po pierwsze, pozwólcie mi wyrazić, że generalnie nie jest to droga do rzeczy w React land. Zwykle to, co chcesz zrobić, to przekazać funkcjonalność dzieciom w rekwizytach i przekazywać powiadomienia od dzieci podczas wydarzeń (lub jeszcze lepiej:) dispatch.

Ale jeśli musisz ujawnić metodę imperatywną na komponencie potomnym, możesz użyć referencji . Pamiętaj, że jest to właz ewakuacyjny i zwykle oznacza, że ​​dostępny jest lepszy projekt.

Wcześniej referencje były obsługiwane tylko dla komponentów opartych na klasie. Wraz z pojawieniem się React Hooks , już tak nie jest

Korzystanie z haków i składników funkcji ( >= [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>

Dokumentacja dla useImperativeHandle()jest tutaj :

useImperativeHandledostosowuje wartość instancji, która podczas używania jest eksponowana na komponenty nadrzędne ref.

Korzystanie ze składników klasy ( >= [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>

Starsze API ( <= [email protected])

Dla celów historycznych, oto styl oparty na wywołaniu zwrotnym, którego używałbyś w wersjach React przed 16.3:

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
źródło
23
Jestem zmęczony, ale kończę na tym błędzie „_this2.refs.child.getAlert nie jest funkcją”
N8FURY
22
Jest tak, ponieważ connectzwraca komponent wyższego rzędu, który otacza oryginalną instancję. getWrappedInstance()Najpierw musisz zadzwonić do podłączonego komponentu, aby uzyskać oryginalny komponent. Następnie możesz wywołać w tym celu metody instancji.
rossipedia,
16
To nie jest naprawdę dobry wzór. Nie wspominając już o referencjach ciągów, które są marszczone. Lepiej jest przekazywać rekwizyty do elementu potomnego, a następnie kliknięcie przycisku nadrzędnego powoduje zmianę stanu rodzica i przekazanie elementu stanu do elementu podrzędnego, który spowoduje wyzwolenie elementu podrzędnego componentWillReceiveProps, i użycie go jako wyzwalacza.
ffxsam
7
Nie, zwykle nie jest to najlepszy wzór, jest to raczej właz ratunkowy, gdy go potrzebujesz i powinien być używany tylko w nagłych wypadkach. Również ta odpowiedź została napisana, gdy referencje ciągów były jeszcze w pobliżu, i masz rację, że nie są one obecnie „właściwym” sposobem robienia rzeczy.
rossipedia
34
Jeśli najlepszą praktyką jest stworzenie labiryntu logiki, aby zrobić coś tak prostego jak wywołanie metody komponentu podrzędnego - wtedy nie zgadzam się z najlepszą praktyką.
aaaaaa
149

Możesz użyć innego wzoru tutaj:

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>
  );
 }
}

Robi to, aby ustawić clickChildmetodę rodzica, gdy dziecko jest zamontowane. W ten sposób, gdy klikniesz przycisk w rodzicu, zadzwoni on, clickChildktóry wywołuje dziecko getAlert.

Działa to również wtedy, gdy twoje dziecko jest owinięte, connect()więc nie potrzebujesz getWrappedInstance()hacka.

Zauważ, że nie możesz użyć onClick={this.clickChild}w rodzicu, ponieważ gdy rodzic jest renderowany, dziecko nie jest zamontowane, więc this.clickChildnie jest jeszcze przypisane. Korzystanie onClick={() => this.clickChild()}jest w porządku, ponieważ po kliknięciu przycisk this.clickChildpowinien być już przypisany.

murowanie
źródło
5
Rozumiem _this2.clickChild is not a functiondlaczego?
tatsu
1
nevermind to zadziałało dla mnie: github.com/kriasoft/react-starter-kit/issues/…
tatsu
5
żadne nie działało. tylko ta odpowiedź działała: github.com/kriasoft/react-starter-kit/issues/…
tatsu
2
To ciekawa technika. Jest dość czysty i nie łamie żadnych zasad. Ale myślę, że twoja odpowiedź byłaby bardziej kompletna (i spełniłaby oczekiwania), gdybyś dodał powiązanie. Tak bardzo podobała mi się odpowiedź, że opublikowałem ją na ten temat związany z Githubem .
joeytwiddle
7
To powinna być prawdziwa odpowiedź
Jesus Gomez
27

https://facebook.github.io/react/tips/expose-component-functions.html aby uzyskać więcej odpowiedzi, zobacz tutaj Wywołanie metod w komponentach React child

Przyglądając się referencjom komponentu „przyczyny”, przełamujesz enkapsulację i uniemożliwiasz refaktoryzację tego komponentu bez dokładnego zbadania wszystkich używanych miejsc. Z tego powodu zdecydowanie zalecamy traktowanie referencji jako prywatnych dla komponentu, podobnie jak state.

Zasadniczo dane powinny być przekazywane w dół drzewa za pomocą rekwizytów. Istnieje kilka wyjątków od tego (np. Wywoływanie .focus () lub uruchamianie jednorazowej animacji, która tak naprawdę nie „zmienia” stanu), ale za każdym razem, gdy ujawniasz metodę o nazwie „set”, rekwizyty są zwykle lepszy wybór. Postaraj się, aby wewnętrzny komponent wejściowy martwił się jego rozmiarem i wyglądem, aby żaden z jego przodków tego nie robił.

Mike Tronic
źródło
5
Oto źródło tej odpowiedzi: dyskusji.reactjs.org/t/… . Nie ma problemów z cytowaniem innych, ale przynajmniej należy podać jakieś odniesienia.
Jodo,
1
Jak dokładnie to przerywa enkapsulację bardziej niż rekwizyty?
Timmmm
15

Alternatywna metoda z useEffect:

Rodzic:

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

Dzieci:

useEffect(() => {
    refresh(); //children function of interest
  }, [props.refresh]);
tonymayoral
źródło
2
To powinno uzyskać więcej głosów
Ali Al Amine
Ps. Jeśli chcesz tylko ponownie renderować formularz (na przykład, aby zresetować pola wprowadzania), nie musisz nawet uwzględniać useEffect, możesz po prostu wysłać prop do zmiany komponentu
Matt Fletcher
8

Możemy użyć referencji w inny sposób, ponieważ-

Utworzymy element nadrzędny, który wyrenderuje <Child/>komponent. Jak widać, komponent, który będzie renderowany, musisz dodać atrybut ref i podać dla niego nazwę.
Następnie triggerChildAlertfunkcja znajdująca się w klasie nadrzędnej uzyska dostęp do właściwości refs tego kontekstu (gdy triggerChildAlertfunkcja zostanie uruchomiona, uzyska dostęp do odwołania potomnego i będzie mieć wszystkie funkcje elementu potomnego).

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>
        );
    }
}  

Teraz komponent podrzędny, jak teoretycznie zaprojektowano wcześniej, będzie wyglądał następująco:

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

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

Oto kod źródłowy -
Nadzieja ci pomoże!

S.Yadav
źródło
1
Referencje ciągów są przestarzałe. reagjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs
Cory McAboy
4

Jeśli robisz to po prostu dlatego, że chcesz, aby Dziecko miało cechę nadającą się do ponownego wykorzystania dla swoich rodziców, możesz zamiast tego rozważyć użycie tego za pomocą rekwizytów renderujących .

Ta technika faktycznie wywraca konstrukcję do góry nogami. ChildTeraz zawija rodzica, więc mam przemianowano ją AlertTraitponiżej. Zachowałem nazwę Parentciągłości, chociaż tak naprawdę nie jest to teraz rodzic.

// 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>
    );
  }
}

W tym przypadku AlertTrait zapewnia jedną lub więcej cech, które przekazuje jako rekwizyty do dowolnego elementu podanego w renderComponentrekwizytach.

Rodzic otrzymuje doAlertjako rekwizyt i może go wywołać w razie potrzeby.

(Dla jasności nazwałem rekwizyt renderComponentw powyższym przykładzie. Ale w dokumentach React powiązanych powyżej po prostu go nazywają render.)

Komponent Trait może renderować elementy otaczające obiekt nadrzędny w funkcji renderowania, ale nie renderuje niczego w obiekcie nadrzędnym. W rzeczywistości mógłby renderować rzeczy wewnątrz rodzica, jeśli przekazałby inny rekwizyt (np. renderChild) Rodzicowi, którego rodzic mógłby następnie użyć podczas metody renderowania.

To nieco różni się od tego, o co poprosił PO, ale niektórzy ludzie mogą tu skończyć (tak jak my), ponieważ chcieli stworzyć cechę wielokrotnego użytku i uważali, że element potomny jest dobrym sposobem na zrobienie tego.

joeytwiddle
źródło
Tutaj znajduje się przydatna lista wzorów do tworzenia cech wielokrotnego użytku: reagjs.org/blog/2016/07/13/…
joeytwiddle
Co jeśli masz N stoperów i jeden przycisk, aby je wszystkie ponownie uruchomić. W jaki sposób renderowanie rekwizytów jest tu przydatne?
vsync
@vsync Nie jestem pewien, czy ta metoda może pomóc w Twoim zadaniu. Ale odpowiedź brickingupa może pomóc. Pamiętaj, że ustawili się, this.clickChild = clickale wiele stoperów przejdzie przez wiele funkcji, więc musisz zapisać je wszystkie:this.watchRestartFuncs[watchId] = restartWatch
joeytwiddle
1

Możesz to łatwo osiągnąć w ten sposób

Kroki-

  1. Utwórz zmienną boolowską w stanie w klasie nadrzędnej. Zaktualizuj to, jeśli chcesz wywołać funkcję.
  2. Utwórz zmienną prop i przypisz zmienną boolean.
  3. Z komponentu potomnego uzyskaj dostęp do tej zmiennej przy użyciu rekwizytów i wykonaj wybraną metodę, mając warunek if.

    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
źródło
Możesz to zrobić w piaskownicy. Wygląda na to, że skończysz z nieskończoną pętlą, ponieważ metoda potomna będzie ciągle działać, ponieważ stan nadrzędny jest ustawiony na true.
Isaac Pak
@IsaacPak Tak, dlatego zostawiłem tam komentarz, mówiąc, że musisz zaktualizować stan zgodnie z własnymi wymaganiami. Wtedy nie będzie działać jako nieskończona pętla.
Kusal Kithmal
1

Używam useEffecthaka, aby przezwyciężyć ból głowy związany z robieniem tego wszystkiego, więc teraz przekazuję zmienną dziecku w następujący sposób:

<ParentComponent>
 <ChildComponent arbitrary={value} />
</ParentComponent>
useEffect(() => callTheFunctionToBeCalled(value) , [value]);
Czarna Mamba
źródło
1

Nie byłem zadowolony z żadnego z przedstawionych tutaj rozwiązań. W rzeczywistości istnieje bardzo proste rozwiązanie, które można wykonać przy użyciu czystego Javascript bez polegania na niektórych funkcjach React innych niż podstawowy obiekt rekwizytów - i daje to korzyść komunikacji w obu kierunkach (rodzic -> dziecko, dziecko -> rodzic). Musisz przekazać obiekt z komponentu nadrzędnego do komponentu podrzędnego. Ten obiekt nazywam „odniesieniem dwukierunkowym” lub w skrócie biRef. Zasadniczo obiekt zawiera odwołanie do metod w obiekcie nadrzędnym, które rodzic chce udostępnić. Komponent potomny dołącza metody do obiektu, który rodzic może wywołać. Coś takiego:

// 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>;
}

Inną zaletą tego rozwiązania jest to, że można dodać o wiele więcej funkcji w obiekcie nadrzędnym i podrzędnym, przekazując je z obiektu nadrzędnego dziecku przy użyciu tylko jednej właściwości.

Ulepszenie w stosunku do powyższego kodu polega na tym, aby nie dodawać funkcji nadrzędnej i podrzędnej bezpośrednio do obiektu biRef, ale raczej do elementów podrzędnych. Funkcje nadrzędne należy dodać do elementu o nazwie „rodzic”, a funkcje podrzędne należy dodać do elementu o nazwie „dziecko”.

// 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>;
}

Umieszczając funkcje nadrzędne i podrzędne w oddzielnych elementach obiektu biRef, będziesz mieć czystą separację między nimi i łatwo zobaczysz, które należą do nadrzędnego lub podrzędnego. Pomaga to również zapobiec przypadkowemu zastąpieniu przez element podrzędny funkcji nadrzędnej, jeśli ta sama funkcja pojawia się w obu.

Ostatnią rzeczą jest to, że jeśli zauważysz, komponent nadrzędny tworzy obiekt biRef za pomocą var, podczas gdy komponent potomny uzyskuje do niego dostęp poprzez obiekt rekwizytów. Może być kuszące, aby nie definiować obiektu biRef w obiekcie nadrzędnym i uzyskiwać do niego dostęp od jego obiektu nadrzędnego za pomocą własnego parametru rekwizytów (co może mieć miejsce w hierarchii elementów interfejsu użytkownika). Jest to ryzykowne, ponieważ dziecko może uważać, że funkcja, którą wywołuje na rodzicu, należy do rodzica, kiedy może faktycznie należeć do dziadka. Nie ma w tym nic złego, o ile jesteś tego świadomy. O ile nie masz powodu, aby wspierać hierarchię poza relacją rodzic-dziecko, najlepiej utworzyć biRef w komponencie nadrzędnym.

AndroidDev
źródło
0

Myślę, że najbardziej podstawowym sposobem wywoływania metod jest ustawienie żądania na komponencie potomnym. Następnie, gdy tylko dziecko obsługuje żądanie, wywołuje metodę wywołania zwrotnego w celu zresetowania żądania.

Mechanizm resetowania jest niezbędny, aby móc wysłać to samo żądanie wiele razy po sobie.

W komponencie nadrzędnym

W metodzie renderowania elementu nadrzędnego:

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

Rodzic potrzebuje 2 metod, aby komunikować się ze swoim dzieckiem w 2 kierunkach.

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

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

W komponencie potomnym

Dziecko aktualizuje swój stan wewnętrzny, kopiując żądanie z rekwizytów.

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;
}

Następnie w końcu obsługuje żądanie i wysyła reset do rodzica:

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

  const { onRequestHandled } = this.props;
  if (onRequestHandled != null) onRequestHandled();
}
bvdb
źródło
0

Innym sposobem wyzwalania funkcji potomnej od rodzica jest użycie componentDidUpdatefunkcji w komponencie potomnym. Przekazuję rekwizyt triggerChildFuncod rodzica do dziecka, którym początkowo jest null. Wartość zmienia się w funkcję po kliknięciu przycisku, a dziecko zauważa zmianę componentDidUpdatei wywołuje własną funkcję wewnętrzną.

Ponieważ prop triggerChildFunczmienia się w funkcji, otrzymujemy również wywołanie zwrotne do rodzica. Jeśli rodzic nie musi wiedzieć, kiedy funkcja jest wywoływana, wartość triggerChildFuncmoże na przykład zmienić z nullna truezamiast.

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
źródło
0

Oto moje demo: https://stackblitz.com/edit/react-dgz1ee?file=styles.css

Używam useEffectdo wywołania metod komponentu potomnego. Próbowałem z, Proxy and Setter_Getterale zdecydowanie useEffectwydaje się być wygodniejszym sposobem na wywołanie metody potomnej od rodzica. Aby Proxy and Setter_Gettergo użyć , wydaje się, że istnieje pewna subtelność, którą należy najpierw pokonać, ponieważ element najpierw renderowany jest elementem objectLike poprzez ref.current return => <div/>specyfikę. Jeśli chodzi o to useEffect, możesz także wykorzystać to podejście, aby ustawić stan rodzica w zależności od tego, co chcesz zrobić z dziećmi.

W linku do dema, który podałem, znajdziesz mój pełny kod ReactJS z moimi szkicami w środku, dzięki czemu możesz docenić przebieg mojego rozwiązania.

Dostarczam ci mój fragment ReactJS tylko z odpowiednim kodem. :

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>
       )
}
Webwoman
źródło
0

Jesteśmy zadowoleni z niestandardowego haka, który nazywamy useCounterKey. Po prostu ustawia counterKey lub klucz, który liczy się od zera. Zwracana funkcja resetuje klawisz (tzn. Przyrost). (Uważam, że jest to najbardziej idiomatyczny sposób resetowania komponentu w React - wystarczy uderzyć klawiszem).

Jednak ten haczyk działa również w każdej sytuacji, w której chcesz wysłać klientowi jednorazową wiadomość, aby coś zrobić. Np. Używamy go do skupienia kontroli nad dzieckiem na określonym zdarzeniu nadrzędnym - po prostu automatycznie ustawia ostrość za każdym razem, gdy klucz jest aktualizowany. (Jeśli potrzeba więcej rekwizytów, można je ustawić przed zresetowaniem klucza, aby były dostępne, gdy zdarzenie się wydarzy.)

Ta metoda ma trochę krzywej uczenia się b / c, nie jest tak prosta jak typowa procedura obsługi zdarzeń, ale wydaje się najbardziej idiomatycznym sposobem radzenia sobie z tym w React, który znaleźliśmy (ponieważ klawisze już działają w ten sposób). Zdecyduj się na opinie na temat tej metody, ale działa ona dobrze!

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

Przykładowe zastosowania:

// 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]);

  // ...
}

( Podziękowania dla Kent Dodds za koncepcję counterKey .)

Freewalker
źródło
-1

Oto błąd? uważać: zgadzam się z rozwiązaniem rossipedia za pomocą forwardRef, useRef, useImperativeHandle

W Internecie jest kilka dezinformacji, które mówią, że referencje można tworzyć tylko z komponentów klasy React, ale rzeczywiście można użyć komponentów funkcji, jeśli użyje się wyżej wspomnianych haków. Uwaga: haki zadziałały dla mnie dopiero po zmianie pliku, aby nie używał on funkcjiRouter () podczas eksportowania komponentu. To znaczy zmiana z

export default withRouter(TableConfig);

zamiast tego być

export default TableConfig;

Z perspektywy czasu metoda withRouter () i tak nie jest potrzebna w przypadku takiego komponentu, ale zwykle nic nie szkodzi w jego posiadaniu. Mój przypadek użycia polega na tym, że utworzyłem komponent do utworzenia tabeli do obsługi przeglądania i edycji wartości konfiguracji, i chciałem móc powiedzieć temu komponentowi potomnemu, aby zresetował jego wartości stanu za każdym razem, gdy wciśnięty został przycisk resetowania formularza nadrzędnego. UseRef () nie mógł poprawnie pobrać ref lub ref.current (ciągle uzyskiwał wartość null), dopóki nie usunęłem withRouter () z pliku zawierającego mój komponent potomny TableConfig

DeltaPng
źródło