Как получить доступ к методам компонентов извне в ReactJS?

183

Почему я не могу получить доступ к методам компонента извне в ReactJS? Почему это невозможно и есть ли способ решить это?

Рассмотрим код:

var Parent = React.createClass({
    render: function() {
        var child = <Child />;
        return (
            <div>
                {child.someMethod()} // expect "bar", got a "not a function" error.
            </div>
        );
    }
});

var Child = React.createClass({
    render: function() {
        return (
            <div>
                foo
            </div>
        );
    },
    someMethod: function() {
        return 'bar';
    }
});

React.renderComponent(<Parent />, document.body);
user1518183
источник
Может тебе нужно Pubsub?
slideshowp2

Ответы:

203

React предоставляет интерфейс для того, что вы пытаетесь сделать с помощью refатрибута . Назначьте компонент a ref, и его currentатрибут будет вашим пользовательским компонентом:

class Parent extends React.Class {
    constructor(props) {
        this._child = React.createRef();
    }

    componentDidMount() {
        console.log(this._child.current.someMethod()); // Prints 'bar'
    }

    render() {
        return (
            <div>
                <Child ref={this._child} />
            </div>
        );
    }
}

Примечание : это будет работать, только если дочерний компонент объявлен как класс, согласно документации, найденной здесь: https://facebook.github.io/react/docs/refs-and-the-dom.html#adding-a- реф-к-а-класс компонент

Обновление 2019-04-01: Изменен пример использования класса и createRefпоследних документов React.

Обновление 2016-09-19: Изменен пример использование рефа обратного вызова каждого из руководства по refатрибутам Струнных документов.

Росс Аллен
источник
Таким образом, единственный способ связи между двумя дочерними компонентами будет иметь оба refs и метод proxy для общего родителя?
elQueFaltaba
15
React поощряет компоненты, управляемые данными. Позвольте одному дочернему элементу вызвать обратный вызов, который изменяет данные в его предке, и когда эти данные изменятся, другой дочерний элемент получит новый propsи выполнит повторную визуализацию соответственно.
Росс Аллен
@RossAllen, хаха, да, в таком случае вам бы тоже пришлось удалить точку с запятой.
HussienK
@HussienK Я предпочитаю использовать блок, если у функции не должно быть возвращаемого значения, поэтому намерение очевидно для следующего разработчика, который читает код. Изменение этого значения {(child) => this._child = child}приведет к созданию функции, которая всегда возвращалась true, но это значение не используется refатрибутом React .
Росс Аллен
39

Если вы хотите вызывать функции для компонентов извне React, вы можете вызывать их по возвращаемому значению renderComponent:

var Child = React.createClass({…});
var myChild = React.renderComponent(Child);
myChild.someMethod();

Единственный способ получить дескриптор экземпляра React Component вне React - сохранить возвращаемое значение React.renderComponent. Источник .

Сьерд
источник
1
на самом деле это работает для реакции16. Метод рендеринга ReactDOM возвращает ссылку на компонент (или возвращает ноль для компонентов без состояния).
Влад Повалий
37

В качестве альтернативы, если метод Child действительно статичен (не является продуктом текущего состояния реквизита), вы можете определить его staticsи затем обращаться к нему, как если бы вы использовали метод статического класса. Например:

var Child = React.createClass({
  statics: {
    someMethod: function() {
      return 'bar';
    }
  },
  // ...
});

console.log(Child.someMethod()) // bar
Пол О'Шаннесси
источник
1
Источник для этого здесь .
Tirdadc
7

Начиная с React 16.3 React.createRefможно использовать (использовать ref.currentдля доступа)

var ref = React.createRef()

var parent = <div><Child ref={ref} /> <button onClick={e=>console.log(ref.current)}</div>

React.renderComponent(parent, document.body)
сойка
источник
4

Начиная с React 0.12 API немного изменился . Действительный код для инициализации myChild будет следующим:

var Child = React.createClass({…});
var myChild = React.render(React.createElement(Child, {}), mountNode);
myChild.someMethod();
Евгений Сафронов
источник
1

Вы могли бы также сделать это так, не уверен , если это план хорошо: D

class Parent extends Component {
  handleClick() {
    if (this._getAlert !== null) {
      this._getAlert()
    }
  }

  render() {
    return (
      <div>
        <Child>
        {(getAlert, childScope) => (
          <span> {!this._getAlert ? this._getAlert = getAlert.bind(childScope) : null}</span>
        )}
        </Child>
        <button onClick={() => this.handleClick()}> Click me</button>
      </div>
      );
    }
  }

class Child extends Component {
  constructor() {
    super();
    this.state = { count: 0 }
  }

  getAlert() {
    alert(`Child function called state: ${this.state.count}`);
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return this.props.children(this.getAlert, this)
  }
}
swennemen
источник
1

Как упоминалось в некоторых комментариях, ReactDOM.renderбольше не возвращает экземпляр компонента. Вы можете передать refобратный вызов при рендеринге корня компонента, чтобы получить экземпляр, например так:

// React code (jsx)
function MyWidget(el, refCb) {
    ReactDOM.render(<MyComponent ref={refCb} />, el);
}
export default MyWidget;

и:

// vanilla javascript code
var global_widget_instance;

MyApp.MyWidget(document.getElementById('my_container'), function(widget) {
    global_widget_instance = widget;
});

global_widget_instance.myCoolMethod();
mgalgs
источник
-1

Еще один способ так просто:

функция снаружи:

function funx(functionEvents, params) {
  console.log("events of funx function: ", functionEvents);
  console.log("this of component: ", this);
  console.log("params: ", params);
  thisFunction.persist();
}

Свяжите это:

constructor(props) {
   super(props);
    this.state = {};
    this.funxBinded = funx.bind(this);
  }
}

Пожалуйста, ознакомьтесь с полным руководством здесь: Как использовать «это» React Component извне?

Хоу Соун
источник