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


95

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

Наприклад, у цьому 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. Якщо вам потрібно щось повторно використати, створіть окремий загальний помічник у третьому файлі та використовуйте його як для кнопки, так і для компонента реакції.
аромат чаю

Відповіді:


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
Зверніть увагу, що використання поверненого значення візуалізації вважається застарілим і, як очікується, буде вилучено у наступних версіях, щоб забезпечити підвищення продуктивності. Підтримуваний спосіб отримання посилання на об’єкт екземпляра компонента - це додавання властивості, refяка є функцією, що викликається з екземпляром як параметром. Це також дозволяє отримати доступ до об'єктів, які не знаходяться на верхньому рівні, наприклад, якщо ви рендеруєте, <MuiThemeProvider><Hello ref={setHelloRef} /></MuiThemeProvider>ви отримуєте правильне посилання, яке передається вашій setHelloRefфункції, а не одному MuiThemeProvider.
Periata Breatta

44

Ви можете робити подобається

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

1
Насправді просто і функціонально! Як це повинно бути. Я дуже вражений тим, наскільки це просто; справді не думав. Можливо, такий підхід не спрацює так добре, якщо все більше компонентів вам слід. Дякую!
Леонардо Маффі

6
Додавання глобальної змінної не є хорошим рішенням. Детальніше про те, чому глобальні змінні погані тут: wiki.c2.com/?GlobalVariablesAreBad
Сальваторе Заппала

3
Дякуємо за голосування !! Так, глобальна змінна погана, але це спосіб вирішити вашу проблему.
Kushal Jain

Це саме те прагматичне та просте рішення, яке мені потрібно, дякую!
Флоран Дестремау,

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() { ...
},

Надія може допомогти кожному там :)


Ви не можете досягти реквізиту або стану у статичній функції.
Сердар Дегірменці

Добре так, але питання стосувалося доступу до функції alertMessage (), щоб ви могли використовувати HelloElement.alertMessage ().
Дарміс

Зазвичай виклик функції без використання props і state не має ефекту. Але, як ви маєте рацію щодо досягнення функції, я знімаю свій голос
Сердар Дегірменці

2

Ви можете просто додати onClickобробник до div з функцією ( onClickє власною реалізацією ReactonClick ), і ви можете отримати доступ до властивості в{ } фігурних дужках, і з'явиться ваше попереджувальне повідомлення.

Якщо ви хочете визначити статичні методи, які можна викликати в класі компонента - вам слід використовувати статику. Хоча:

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

Приклад коду:

    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

2

С 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.curent.handleClick}>
        Additional Button
      </button>
    </>
  )
}

Щасти...


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);
        });
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.