Викликати метод компонента 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.