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

234

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

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

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

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

Теги:

7 ответов

321
Лучший ответ

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

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

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

Крючки нестабильны и только перед выпуском. API может (и, скорее всего, изменится).

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

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

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

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

Использование компонентов класса (react@stable)

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>
  • 19
    Я устал, но в конечном итоге с этой ошибкой "_this2.refs.child.getAlert не является функцией"
  • 0
    и я должен отметить, что эти компоненты находятся в двух отдельных файлах, и я импортирую дочерний элемент в родительский
Показать ещё 13 комментариев
64

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

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 уже должен быть назначен.

  • 4
    Я получаю _this2.clickChild is not a function почему?
  • 0
    несмотря на это, это сработало для меня: github.com/kriasoft/react-starter-kit/issues/…
Показать ещё 7 комментариев
21

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

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

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

  • 4
    Вот источник этого ответа: обсуждения.reactjs.org/t/… . Нет проблем с цитированием других, но, по крайней мере, вставьте некоторые ссылки.
3

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

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

  • 0
    Здесь есть удобный список шаблонов для создания черт многократного использования : actjs.org/blog/2016/07/13/…
  • 0
    Что делать, если у вас есть N секундомеров и одна кнопка, чтобы перезапустить их все. Как сделать реквизит здесь удобным?
Показать ещё 1 комментарий
2

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

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

Вот источник code-
Надеюсь поможет тебе!

0

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

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

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

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню