Вызов метода компонента React извне

95

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

Например, в этом файле jsfiddle . Я хочу вызвать alertMessageметод по HelloElementссылке.

Есть ли способ добиться этого без написания дополнительных оболочек?

Изменить (скопированный код из JSFiddle)

<div id="container"></div>
<button onclick="onButtonClick()">Click me!</button>
var onButtonClick = function () {

    //call alertMessage method from the reference of a React Element! Something like HelloElement.alertMessage()
    console.log("clicked!");
}

var Hello = React.createClass({displayName: 'Hello',

    alertMessage: function() {
        alert(this.props.name);                             
    },

    render: function() {
        return React.createElement("div", null, "Hello ", this.props.name);
    }
});

var HelloElement = React.createElement(Hello, {name: "World"});

React.render(
    HelloElement,
    document.getElementById('container')
);
Богатый воин
источник
3
Не идеально, но JSFiddle достаточно распространен, чтобы не голосовать против.
Джефф Фэйрли
Мне интересно, какой у вас вариант использования, который оправдывает такое. Это не лучший способ разработать ваше приложение imo. Если вам действительно нужно что-то повторно использовать, создайте отдельный общий помощник в третьем файле и используйте его для своей кнопки, а также для компонента реакции.
teaflavored

Ответы:

56

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

Пример

Вам нужно вызвать функцию при возврате из React.render. Увидеть ниже.

Статический

Взгляните на ReactJS Statics . Обратите внимание, однако, что статическая функция не может получить доступ к данным уровня экземпляра, поэтому thisбудет undefined.

var onButtonClick = function () {
    //call alertMessage method from the reference of a React Element! 
    HelloRendered.alertMessage();
    //call static alertMessage method from the reference of a React Class! 
    Hello.alertMessage();
    console.log("clicked!");
}

var Hello = React.createClass({
    displayName: 'Hello',
    statics: {
        alertMessage: function () {
            alert('static message');
        }
    },
    alertMessage: function () {
        alert(this.props.name);
    },

    render: function () {
        return React.createElement("div", null, "Hello ", this.props.name);
    }
});

var HelloElement = React.createElement(Hello, {
    name: "World"
});

var HelloRendered = React.render(HelloElement, document.getElementById('container'));

Тогда сделай HelloRendered.alertMessage().

Джефф Фэрли
источник
13
Обратите внимание, что использование возвращаемого значения render считается устаревшим и, как ожидается, будет удалено в будущих версиях, чтобы обеспечить повышение производительности. Поддерживаемый способ получения ссылки на объект экземпляра компонента - добавить свойство, refкоторое представляет собой функцию, вызываемую с экземпляром в качестве параметра. Это также позволяет вам получить доступ к объектам, которые не находятся на верхнем уровне, например, при рендеринге <MuiThemeProvider><Hello ref={setHelloRef} /></MuiThemeProvider>вы получаете правильную ссылку, переданную вашей setHelloRefфункции, а не ссылку MuiThemeProvider.
Periata Breatta
Uncaught TypeError: _react2.default.render не является функцией
Партха Пол
46

Вы можете сделать как

import React from 'react';

class Header extends React.Component{

    constructor(){
        super();
        window.helloComponent = this;
    }

    alertMessage(){
       console.log("Called from outside");
    }

    render(){

      return (
      <AppBar style={{background:'#000'}}>
        Hello
      </AppBar>
      )
    }
}

export default Header;

Теперь извне этого компонента вы можете вызвать, как это показано ниже

window.helloComponent.alertMessage();
Кушал Джайн
источник
2
На самом деле просто и функционально! Как и должно быть. Я очень впечатлен тем, насколько это просто; действительно не думал об этом. Вероятно, этот подход не будет работать так хорошо, если вам потребуется все больше и больше компонентов. Спасибо!
Леонардо Маффей
6
Добавление глобальной переменной - не лучшее решение. Подробнее о том, почему глобальные переменные плохи здесь: wiki.c2.com/?GlobalVariablesAreBad
Сальваторе
4
Спасибо за голосование против !! Да, глобальные переменные не подходят, но это способ решить вашу проблему.
Кушал Джайн
1
Это именно то прагматичное и простое решение, которое мне нужно, спасибо!
Флоран Дестремау,
2
это сработало, но не будет работать, если у вас есть несколько одинаковых компонентов на одной странице.
gaurav
26

Я сделал что-то вроде этого:

class Cow extends React.Component {

    constructor (props) {
        super(props);
        this.state = {text: 'hello'};
    }

    componentDidMount () {
        if (this.props.onMounted) {
            this.props.onMounted({
                say: text => this.say(text)
            });
        }
    }

    render () {
        return (
            <pre>
                 ___________________
                < {this.state.text} >
                 -------------------
                        \   ^__^
                         \  (oo)\_______
                            (__)\       )\/\
                                ||----w |
                                ||     ||
            </pre>
        );
    }

    say (text) {
        this.setState({text: text});
    }

}

А потом еще где-нибудь:

class Pasture extends React.Component {

    render () {
        return (
            <div>
                <Cow onMounted={callbacks => this.cowMounted(callbacks)} />
                <button onClick={() => this.changeCow()} />
            </div>
        );
    }

    cowMounted (callbacks) {
        this.cowCallbacks = callbacks;
    }

    changeCow () {
        this.cowCallbacks.say('moo');
    }

}

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

гитаарик
источник
5
Я думаю, вы имели в видуthis.cowCallbacks.say('moo')
Стивен
Кстати, можно перейти thisк обратному вызову (который будет экземпляром Cow) вместо callbacks.
WebBrother
@WebBrother Да, но это было бы еще более
хакерским
6

Поскольку renderметод потенциально не рекомендует возвращаемое значение, рекомендуется теперь присоединить ссылку обратного вызова к корневому элементу. Как это:

ReactDOM.render( <Hello name="World" ref={(element) => {window.helloComponent = element}}/>, document.getElementById('container'));

к которому мы затем можем получить доступ с помощью window.helloComponent, а к любому из его методов можно получить доступ с помощью window.helloComponent.METHOD.

Вот полный пример:

var onButtonClick = function() {
  window.helloComponent.alertMessage();
}

class Hello extends React.Component {
  alertMessage() {
    alert(this.props.name);
  }

  render() {
    return React.createElement("div", null, "Hello ", this.props.name);
  }
};

ReactDOM.render( <Hello name="World" ref={(element) => {window.helloComponent = element}}/>, document.getElementById('container'));
<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="container"></div>
<button onclick="onButtonClick()">Click me!</button>

Бретт ДеВуди
источник
Лучше поместить ссылку в локальное состояние, а не в объект окна ... ref={component => {this.setState({ helloComponent: component; })}} Затем в обработчике кликов ... this.state.helloComponent.alertMessage();
Билл
В дополнение к моему предыдущему комментарию ... или, еще лучше, просто установите метод alertMessage компонента в состояние.
Билл Дэгг
Когда я пытаюсь это сделать, я получаю, Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?и он не привязывает компонент к окну
Партизанский
4
class AppProvider extends Component {
  constructor() {
    super();

    window.alertMessage = this.alertMessage.bind(this);
  }

  alertMessage() {
    console.log('Hello World');
 }
}

Вы можете вызвать этот метод из окна, используя window.alertMessage().

Мигель Бергстрессер
источник
Это сработает, но добавление глобальной переменной не является хорошим решением по многим причинам. Вы можете найти больше информации о том, почему глобальные переменные плохи здесь: wiki.c2.com/?GlobalVariablesAreBad
Сальваторе
3

Если вы работаете в ES6, просто используйте ключевое слово static в вашем методе из вашего примера: static alertMessage: function() { ...
},

Надежда может помочь кому угодно :)

Дармис
источник
Вы не можете достичь реквизита или состояния в статической функции.
Serdar
Хорошо, да, но вопрос был о доступе к функции alertMessage (), чтобы вы могли использовать HelloElement.alertMessage ().
Дармис 06
Обычно вызов функции без использования свойств и состояния не имеет никакого эффекта. Но поскольку вы правы насчет достижения функции, я
Сердар Дегирменчи
3

С участием React hook - useRef



const MyComponent = ({myRef}) => {
  const handleClick = () => alert('hello world')
  myRef.current.handleClick = handleClick
  return (<button onClick={handleClick}>Original Button</button>)
}

MyComponent.defaultProps = {
  myRef: {current: {}}
}

const MyParentComponent = () => {
  const myRef = React.useRef({})
  return (
    <>
      <MyComponent 
        myRef={myRef}
      />
      <button onClick={myRef.current.handleClick}>
        Additional Button
      </button>
    </>
  )
}

Удачи...

Аакаш
источник
2

Вы можете просто добавить onClickобработчик к div с функцией ( onClickэто собственная реализация React onClick), и вы можете получить доступ к свойству в { }фигурных скобках, и появится ваше предупреждающее сообщение.

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

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

Пример кода:

    const Hello = React.createClass({

        /*
            The statics object allows you to define static methods that can be called on the component class. For example:
        */
        statics: {
            customMethod: function(foo) {
              return foo === 'bar';
            }
        },


        alertMessage: function() {
            alert(this.props.name);                             
        },

        render: function () {
            return (
                <div onClick={this.alertMessage}>
                Hello {this.props.name}
                </div>
            );
        }
    });

    React.render(<Hello name={'aworld'} />, document.body);

Надеюсь, это вам немного поможет, потому что я не знаю, правильно ли я понял ваш вопрос, поэтому поправьте меня, если я неправильно его истолковал :)

Данилло Феликсдаал
источник
2

способ 1 using ChildRef :

public childRef: any = React.createRef<Hello>();

public onButtonClick= () => {
    console.log(this.childRef.current); // this will have your child reference
}

<Hello ref = { this.childRef }/>
<button onclick="onButtonClick()">Click me!</button>

Способ 2: using window register

public onButtonClick= () => {
    console.log(window.yourRef); // this will have your child reference
}

<Hello ref = { (ref) => {window.yourRef = ref} }/>`
<button onclick="onButtonClick()">Click me!</button>
Мохидин бин Мохаммед
источник
Метод 1 - очень простой и понятный способ доступа к методам дочерних компонентов. Благодарность!
gabdara
1

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

static async renderComponentAt(componentClass, props, parentElementId){
         let componentId = props.id;
        if(!componentId){
            throw Error('Component has no id property. Please include id:"...xyz..." to component properties.');
        }

        let parentElement = document.getElementById(parentElementId);

        return await new Promise((resolve, reject) => {
            props.ref = (component)=>{
                resolve(component);
            };
            let element = React.createElement(componentClass, props, null);
            ReactDOM.render(element, parentElement);
        });
    }
Стефан
источник