Вызовите дочерний метод от родителя

У меня есть два компонента.

  1. Родительский компонент
  2. Дочерний компонент

Я пытался вызвать метод ребенка из Parent, я пытался таким образом, но не смог получить результат

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

Есть ли способ вызвать метод ребенка от родителя?

Примечание: дочерний и родительский компоненты находятся в двух разных файлах

24 ответа

Решение

Вы можете использовать ссылки

Ранее ссылки поддерживались только для компонентов на основе классов. С появлением React Hooks это уже не так

Использование хуков и функциональных компонентов

import React, { forwardRef, useRef, useImperativeMethods } from '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 
  useImperativeMethods(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>
  );
};

Пример функционирования

Документация для useImperativeMethods() здесь:

useImperativeMethods настраивает значение экземпляра, которое предоставляется родительским компонентам при использовании ref,

Использование компонентов класса

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

Пример функционирования

Устаревший API (React < 16.3)

Для исторических целей, вот стиль на основе обратного вызова, который вы бы использовали с версиями React до 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>

Вы можете использовать другой шаблон здесь:

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

Что он делает, так это устанавливает метод clickChild родителя при монтировании child. Таким образом, когда вы нажимаете кнопку в родительском элементе, она вызывает метод clickChild, который вызывает дочерний метод getAlert.

Это также работает, если ваш ребенок обернут в connect(), поэтому вам не нужен хак getWrappedInstance().

Обратите внимание, что вы не можете использовать onClick={this.clickChild} в parent, потому что при отображении parent потомок не монтируется, поэтому this.clickChild еще не назначен. Использование onClick={() => this.clickChild()} хорошо, потому что при нажатии кнопки this.clickChild уже должен быть назначен.

Альтернативный метод с использованием useEffect:

Родитель:

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

Дети:

useEffect(() => {
    performRefresh(); //children function of interest
  }, [props.refresh]);

Здесь я дам вам четыре возможных комбинации:

  1. Родитель класса | Крючок Ребенок
  2. Крючок Родитель | Класс Ребенок
  3. Крючок Родитель | Крючок Ребенок
  4. Родитель класса | Класс Ребенок

Родитель класса | Крючок Ребенок

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

  render() {
    return (<View>
      <Child ref={this.myRef}/>
      <Button title={'call me'}
              onPress={() => this.myRef.current.childMethod()}/>
    </View>)
  }
}

const Child = React.forwardRef((props, ref) => {

  useImperativeHandle(ref, () => ({
    childMethod() {
      childMethod()
    }
  }))

  function childMethod() {
    console.log('call me')
  }

  return (<View><Text> I am a child</Text></View>)
})

Крючок Родитель | Класс Ребенок

      function Parent(props) {

  const myRef = useRef()

  return (<View>
    <Child ref={myRef}/>
    <Button title={'call me'}
            onPress={() => myRef.current.childMethod()}/>
  </View>)
}

class Child extends React.Component {

  childMethod() {
    console.log('call me')
  }

  render() {
    return (<View><Text> I am a child</Text></View>)
  }
}

Крючок Родитель | Крючок Ребенок

      function Parent(props) {

  const myRef = useRef()

  return (<View>
    <Child ref={myRef}/>
    <Button title={'call me'}
            onPress={() => myRef.current.childMethod()}/>
  </View>)
}

const Child = React.forwardRef((props, ref) => {

  useImperativeHandle(ref, () => ({
    childMethod() {
      childMethod()
    }
  }))

  function childMethod() {
    console.log('call me')
  }

  return (<View><Text> I am a child</Text></View>)
})

Родитель класса | Класс Ребенок

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

  render() {
    return (<View>
      <Child ref={this.myRef}/>
      <Button title={'call me'}
              onPress={() => this.myRef.current.childMethod()}/>
    </View>)
  }
}

class Child extends React.Component {

  childMethod() {
    console.log('call me')
  }

  render() {
    return (<View><Text> I am a child</Text></View>)
  }
}

https://facebook.github.io/react/tips/expose-component-functions.html для получения дополнительных ответов см. здесь Вызов методов на дочерних компонентах React

Изучая ссылки на компонент "причина", вы нарушаете инкапсуляцию и делаете невозможным рефакторинг этого компонента без тщательного изучения всех мест его использования. По этой причине мы настоятельно рекомендуем рассматривать ссылки как частные для компонента, так же как и для состояния.

Как правило, данные должны передаваться по дереву через реквизит. Есть несколько исключений из этого (например, вызов.focus () или запуск одноразовой анимации, которая на самом деле не "меняет" состояние), но каждый раз, когда вы выставляете метод с именем "set", реквизит обычно лучший выбор. Постарайтесь сделать так, чтобы внутренний компонент ввода беспокоился о своем размере и внешнем виде, чтобы никто из его предков не беспокоился.

Меня не удовлетворило ни одно из представленных здесь решений. На самом деле существует очень простое решение, которое может быть выполнено с использованием чистого Javascript, не полагаясь на некоторые функции React, отличные от основного объекта props, и это дает вам возможность общаться в любом направлении (родительский -> дочерний, дочерний -> родительский). Вам необходимо передать объект из родительского компонента в дочерний компонент. Этот объект я называю "двунаправленной ссылкой" или сокращенно biRef. По сути, объект содержит ссылку на методы в родительском элементе, которые родительский объект хочет предоставить. А дочерний компонент присоединяет методы к объекту, который может вызывать родительский компонент. Что-то вроде этого:

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

Другое преимущество этого решения заключается в том, что вы можете добавить гораздо больше функций в родительский и дочерний элементы, передавая их от родительского элемента дочернему, используя только одно свойство.

Улучшение по сравнению с приведенным выше кодом заключается в том, что родительские и дочерние функции не добавляются непосредственно к объекту biRef, а к подчиненным членам. Родительские функции должны быть добавлены к члену с именем "родительский", а дочерние функции должны быть добавлены к члену с именем "дочерний".

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

Поместив родительские и дочерние функции в отдельные члены объекта biRef, вы получите четкое разделение между ними и легко увидите, какие из них принадлежат родительскому или дочернему. Это также помогает предотвратить случайную перезапись дочерним компонентом родительской функции, если одна и та же функция присутствует в обоих.

И последнее: если вы заметили, родительский компонент создает объект biRef с помощью var, тогда как дочерний компонент обращается к нему через объект props. Может возникнуть соблазн не определять объект biRef в родительском элементе и обращаться к нему из его родительского элемента через собственный параметр props (что может иметь место в иерархии элементов пользовательского интерфейса). Это рискованно, потому что ребенок может думать, что функция, которую он вызывает для родителя, принадлежит родителю, хотя на самом деле она может принадлежать дедушке или бабушке. В этом нет ничего плохого, если вы это знаете. Если у вас нет причин для поддержки какой-либо иерархии помимо отношений родитель / потомок, лучше всего создать biRef в родительском компоненте.

Надеюсь, я не повторяю ничего из вышеперечисленного, но как насчет передачи опоры обратного вызова, которая устанавливает функцию в родительском элементе? Это работает и довольно просто. (Добавленный код находится между ////)

class Parent extends Component {
  ///// 
  getAlert = () => {} // initial value for getAlert

  setGetAlertMethod = (newMethod) => {
    this.getAlert = newMethod;
  }
  /////

  render() {
    return (
      <Child setGetAlertMethod={this.setGetAlertMethod}>
        <button onClick={this.getAlert}>Click</button>
      </Child>
      );
    }
  }



class Child extends Component {
  /////
  componentDidMount() {
    this.props.setGetAlertMethod(this.getAlert);
  }
  /////

  getAlert() => {
    alert('clicked');
  }

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

Я использую useEffect, чтобы преодолеть головную боль, связанную со всем этим, поэтому теперь я передаю переменную дочернему элементу следующим образом:

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

ты можешь использовать ref для вызова функции дочернего компонента из родительского

Функциональное компонентное решение

в функциональном компоненте вы должны использовать useImperativeHandle для получения ссылки на ребенка, как показано ниже

      import React, { forwardRef, useRef, useImperativeHandle } from 'react';
export default function ParentFunction() {
    const childRef = useRef();
    return (
        <div className="container">
            <div>
                Parent Component
            </div>
            <button
                onClick={() => { childRef.current.showAlert() }}
            >
            Call Function
            </button>
            <Child ref={childRef}/>
        </div>
    )
}
const Child = forwardRef((props, ref) => {
    useImperativeHandle(
        ref,
        () => ({
            showAlert() {
                alert("Child Function Called")
            }
        }),
    )
    return (
       <div>Child Component</div>
    )
})

Компонент класса Решение

Child.js

      import s from './Child.css';

class Child extends Component {
 getAlert() {
    alert('clicked');
 }
 render() {
  return (
    <h1>Hello</h1>
  );
 }
}

export default Child;

Parent.js

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

Для функциональных компонентов проще всего

Родительский компонент

parent.tsx

      import React, { useEffect, useState, useRef } from "react";
import child from "../../child"

const parent: React.FunctionComponent = () => {
  const childRef: any = useRef();
}

const onDropDownChange: any = (event): void => {
    const target = event.target;
    childRef.current.onFilterChange(target.value);
  };

 return <child ref={childRef} />

export default parent;

Дочерний компонент

child.tsx

      import React, {   useState,   useEffect,   forwardRef,   useRef,   useImperativeHandle, } from "react";

const Child = forwardRef((props, ref) => {
 useImperativeHandle(ref, () => ({
    onFilterChange(id) {
      console.log("Value from parent", id)
    },
  }));
})

Child.displayName = "Child";

export default Child;

Мы можем использовать ссылки другим способом, так как

Мы собираемся создать родительский элемент, он будет визуализировать <Child/> составная часть. Как видите, для компонента, который будет отображаться, нужно добавить атрибут ref и указать для него имя.
Затем triggerChildAlert Функция, расположенная в родительском классе, получит доступ к свойству refs этого контекста (когда triggerChildAlert Функция триггера получит доступ к дочерней ссылке и будет иметь все функции дочернего элемента).

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

Теперь дочерний компонент, как теоретически разработано ранее, будет выглядеть так:

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

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

Вот исходный код
Надеюсь поможет тебе!

Если вы делаете это просто потому, что хотите, чтобы Child предоставил своим родителям повторно используемую черту, то вы можете вместо этого сделать это с помощью render-props.

Эта техника фактически переворачивает конструкцию с ног на голову. Child теперь оборачивает родителя, поэтому я переименовал его в AlertTrait ниже. Я сохранил имя Parent для преемственности, хотя это не совсем родитель сейчас.

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

В этом случае AlertTrait предоставляет одну или несколько черт, которые он передает в качестве реквизита для любого компонента, который ему был дан в его renderComponent двигательный

Родитель получает doAlert как опора, и может назвать это при необходимости.

(Для ясности я назвал опору renderComponent в приведенном выше примере. Но в документах React, связанных выше, они просто называют это render.)

Компонент Trait может визуализировать вещи, окружающие Parent, в своей функции render, но он ничего не визуализирует внутри родителя. На самом деле это может сделать вещи внутри Родителя, если он прошел другую опору (например, renderChild) к родителю, который родитель мог бы затем использовать во время своего метода рендеринга.

Это несколько отличается от того, о чем просил OP, но некоторые люди могут оказаться здесь (как и мы), потому что они хотели создать черту многократного использования и думали, что дочерний компонент - хороший способ сделать это.

Этот шаблон похож на ответ @brickingup . Но в этой версии вы можете установить столько дочерних действий, сколько захотите.

      import { useEffect } from "react";

export const Parent = () => {
  const childEvents = { click: () => {} };

  return (
    <div onClick={() => childEvents.click()}>
      <Child events={childEvents}></Child>
    </div>
  );
};

export const Child = (props) => {
  const click = () => {
    alert("click from child");
  };

  useEffect(() => {
    if (props.events) {
      props.events.click = click;
    }
  }, []);

  return <span>Child Component</span>;
};

Логика проста.

Create a function in parent using child or use ref.

Я предпочитаю первое. Есть несколько способов сделать это.

При использовании функциональных компонентов

В родительском

      function Parent(){
  const [functionToCall, createFunctionToCall] = useState(()=>()=>{})

  return (
   <Child createFunctionToCall={createFunctionToCall} />
  )
}

В детстве

      function Child({createFunctionToCall}){
  useEffect(()=>{
    function theFunctionToCall(){
      // do something like setting something
      // don't forget to set dependancies properly.
    }
    createFunctionToCall(()=>theFunctionToCall)
  },[createFunctionToCall])
}

Родительский компонент

      import Child from './Child'

export default function Parent(props) {
    const [childRefreshFunction, setChildRefreshFunction] = useState(null);

    return (
        <div>
            <button type="button" onClick={() => {
                childRefreshFunction();
            }}>Refresh child</button>
            <Child setRefreshFunction={(f) => {
                setChildRefreshFunction(f);
            }} />
        </div>
    )
}

Дочерний компонент

      export default function Child(props) {

    useEffect(() => {
        props.setRefreshFunction(() => refreshMe);
    }, []);

    function refreshMe() {
        fetch('http://example.com/data.json')....
    };

    return (
        <div>
            child
        </div>
    )
}

Нам нравится настраиваемый хук, который мы называем useCounterKey. Он просто устанавливает counterKey или ключ, который ведет отсчет с нуля. Функция, которую он возвращает, сбрасывает ключ (т.е. увеличивает). (Я считаю, что это самый идиоматический способ сброса компонента в React - просто нажмите клавишу.)

Однако этот хук также работает в любой ситуации, когда вы хотите отправить клиенту одноразовое сообщение, чтобы что-то сделать. Например, мы используем его, чтобы сфокусировать элемент управления в дочернем элементе на определенном родительском событии - он просто автоматически фокусируется при обновлении ключа. (Если необходимо больше свойств, их можно установить до сброса ключа, чтобы они были доступны, когда произойдет событие.)

Этот метод требует некоторого обучения, потому что он не такой простой, как типичный обработчик событий, но кажется наиболее идиоматическим способом справиться с этим в React, который мы нашли (поскольку ключи уже работают таким образом). Def открыт для отзывов об этом методе, но он работает хорошо!

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

Примеры использования:

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

  // ...
}

( Благодарим Кента Доддса за концепцию counterKey.)

Вы можете очень легко применить эту логику, используя свой дочерний компонент в качестве настраиваемого хука реакции .

Как это реализовать?

  • Ваш ребенок возвращает функцию.

  • Ваш ребенок возвращает JSON: {function, HTML или другие значения} в качестве примера.

In the example doesn't make sense to apply this logic but it is easy to see:

Вот моя демонстрация: https://stackblitz.com/edit/react-dgz1ee?file=styles.css

Я использую useEffectдля вызова методов дочернего компонента. Я пробовал сProxy and Setter_Getter но сор далеко useEffectкажется более удобным способом вызвать дочерний метод от родителя. ИспользоватьProxy and Setter_Getter кажется, есть некоторая тонкость, которую нужно преодолеть в первую очередь, потому что элемент, который сначала отображается, является элементом objectLike через ref.current return => <div/>специфика. ОтносительноuseEffect, вы также можете использовать этот подход для установки родительского состояния в зависимости от того, что вы хотите делать с потомками.

В приведенной мной демонстрационной ссылке вы найдете мой полный код ReactJS с моими черновиками внутри, чтобы вы могли оценить рабочий процесс моего решения.

Здесь я предоставляю вам свой фрагмент ReactJS только с соответствующим кодом.:

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

Другой способ вызвать дочернюю функцию от родителя - использовать componentDidUpdateфункция в дочернем компоненте. Я передаю реквизитtriggerChildFunc от родителя к ребенку, что изначально null. Значение изменяется на функцию при нажатии кнопки, и Ребенок замечает, что изменение вcomponentDidUpdate и вызывает свою внутреннюю функцию.

Поскольку опора triggerChildFuncизменения функции, мы также получаем обратный вызов для Родителя. Если Родителю не нужно знать, когда функция вызывается, значениеtriggerChildFunc может, например, измениться с null к 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

Вы можете легко достичь этого

Steps-

  1. Создайте логическую переменную в состоянии в родительском классе. Обновите это, когда вы хотите вызвать функцию.
  2. Создайте переменную prop и присвойте логическую переменную.
  3. Из дочернего компонента обращайтесь к этой переменной с помощью props и выполняйте нужный метод, используя условие 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>
        );
       }
    }
    

я пытался использовать createRefили же useRef. Почему-то все возвращаются null.

Во- вторых, этот ответ предлагает передать a, который устанавливает functionэто кажется мне самым разумным. Но если ваш дочерний компонент используется в нескольких местах, вы должны добавить этот дополнительный propв другие места. Кроме того, если вы хотите вызвать метод во внучке, этот метод может быть слишком многословным или громоздким.

Поэтому я сделал свое собственное хранилище функций очень примитивным способом.

Ниже functionStore.jsфайл

      const fns = {};

export function setFn(componentName, fnName, fn) {
  if (fns[componentName]) {
    fns[componentName][fnName] = fn;
  } else {
    fns[componentName] = { fnName: fn };
  }
}

export function callFn(componentName, fnName) {
  fns[componentName][fnName]();
}

Я просто задаю функции, которые нужно вызывать из любого компонента.

      import { setFn } from "./functionStore";
export class AComponent extends React.Component {
  componentDidMount() {
    setFn("AComponent", "aFunc", this.aFunc);
  }
  aFunc = () => { console.log("aFunc is called!"); };
}

Затем я просто вызываю его из другого компонента

      import { callFn } from "./functionStore";
export class BComponent extends React.Component {
  
  // just call the function
  bFunc = () => { 
    callFn("AComponent", "aFunc");
  };
}

Одним из недостатков является то, что вызываемая функция не должна иметь параметров. Но это тоже можно как-то исправить. В настоящее время мне не нужно передавать параметры.

Вы можете сделать Inveritance Inversion (посмотрите его здесь: https://medium.com/@franleplant/react-higher-order-components-in-depth-cf9032ee6c3e). Таким образом, у вас есть доступ к экземпляру компонента, который вы хотите обернуть (таким образом, вы сможете получить доступ к его функциям)

Я думаю, что самый простой способ вызова методов - это установить запрос на дочерний компонент. Затем, как только ребенок обрабатывает запрос, он вызывает метод обратного вызова для сброса запроса.

Механизм сброса необходим, чтобы иметь возможность отправлять один и тот же запрос несколько раз друг за другом.

В родительском компоненте

В методе рендеринга родителя:

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

Родителю нужно 2 метода, чтобы общаться с ребенком в 2 направлениях.

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

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

В дочернем компоненте

Ребенок обновляет свое внутреннее состояние, копируя запрос с реквизита.

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

Затем, наконец, он обрабатывает запрос и отправляет сброс родителю:

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

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

Вот ошибка? на что обратить внимание: я согласен с решением rossipedia с использованием forwardRef, useRef, useImperativeHandle

В Интернете есть некоторая дезинформация, в которой говорится, что ссылки могут быть созданы только из компонентов React Class, но вы действительно можете использовать функциональные компоненты, если используете вышеупомянутые хуки. Обратите внимание, хуки сработали у меня только после того, как я изменил файл, чтобы он не использовался withRouter() при экспорте компонента. Т.е. изменение с

export default withRouter(TableConfig);

вместо этого быть

export default TableConfig;

Оглядываясь назад, в любом случае withRouter() не нужен для такого компонента, но обычно это не повреждает ничего, в чем он находится. Мой вариант использования состоит в том, что я создал компонент для создания таблицы для обработки просмотра и редактирования значений конфигурации, и я хотел иметь возможность сообщить этому дочернему компоненту, чтобы он сбрасывал значения своего состояния при каждом нажатии кнопки сброса родительской формы. UseRef() не получит должным образом ref или ref.current (продолжает получать значение null), пока я не удалю withRouter() из файла, содержащего мой дочерний компонент TableConfig

Другие вопросы по тегам