it-roy-ru.com

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

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

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

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

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

232
Evo SL

Прежде всего, позвольте мне выразить, что это, как правило, не способ идти о вещах в React Land. Обычно вы хотите передать функциональность дочерним элементам в подпорках и передать уведомления от дочерних элементов в событиях.

Но если вы должны предоставить императивный метод для дочернего компонента, вы можете использовать refs . Помните, что это аварийный люк, который обычно указывает на лучший дизайн.

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

Использование хуков и функциональных компонентов (>= [email protected])

import React, { forwardRef, useRef, useImperativeHandle } 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
  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>
  );
};

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

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

useImperativeHandle настраивает значение экземпляра, которое предоставляется родительским компонентам при использовании 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 (<= [email protected])

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

323
rossipedia

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

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 родительского элемента, когда монтируется дочерний элемент. Таким образом, когда вы нажимаете кнопку в родительском элементе, она вызывает метод clickChild, который вызывает дочерний метод getAlert.

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

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

68
brickingup

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

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

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

22
Mike Tronic

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

Мы собираемся создать элемент Parent, он будет отображать компонент <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>
        );
    }
}

Вот исходный код -
Надежда поможет вам!

4
S.Yadav

Если вы делаете это просто потому, что хотите, чтобы 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 в качестве реквизита и может вызывать его при необходимости.

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

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

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

3
joeytwiddle

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

0
szpada87

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

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

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

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

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();
}
0
bvdb

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

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>
        );
       }
    }
    
0
Kusal Kithmal