Chiama un metodo del componente React dall'esterno


95

Voglio chiamare un metodo esposto da un componente React dall'istanza di un elemento React.

Ad esempio, in questo jsfiddle . Voglio chiamare il alertMessagemetodo dal HelloElementriferimento.

C'è un modo per ottenere questo risultato senza dover scrivere wrapper aggiuntivi?

Modifica (codice copiato da 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
Non è l'ideale, ma JSFiddle è abbastanza comune da non garantire un voto negativo.
Jeff Fairley

Mi chiedo quale potrebbe essere il tuo caso d'uso che giustifichi una cosa del genere. Questo non è un buon modo per progettare la tua applicazione imo. Se hai bisogno di riutilizzare qualcosa, crea un aiuto comune separato in un terzo file e usalo per il tuo pulsante e per il tuo componente di reazione.
tè aromatizzato

Risposte:


56

Esistono due modi per accedere a una funzione interna. Uno, a livello di istanza, come desideri, un altro livello statico.

Esempio

Devi chiamare la funzione al ritorno da React.render. Vedi sotto.

Statico

Dai un'occhiata a ReactJS Statics . Si noti, tuttavia, che una funzione statica non può accedere ai dati a livello di istanza, quindi thissarebbe 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'));

Allora fallo HelloRendered.alertMessage().


13
Si noti che l'utilizzo del valore restituito del rendering è considerato deprecato e dovrebbe essere rimosso nelle versioni future per consentire miglioramenti delle prestazioni. Il modo supportato per ottenere un riferimento all'oggetto istanza del componente è aggiungere una proprietà refche è una funzione chiamata con l'istanza come parametro. Questo ti permette anche di accedere agli oggetti che non sono al livello più alto, ad esempio se stai eseguendo il rendering <MuiThemeProvider><Hello ref={setHelloRef} /></MuiThemeProvider>ottieni il riferimento corretto passato alla tua setHelloReffunzione piuttosto che uno a MuiThemeProvider.
Periata Breatta

Uncaught TypeError: _react2.default.render non è una funzione
Partha Paul

46

Puoi fare mi piace

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;

Ora dall'esterno di questo componente puoi chiamare in questo modo di seguito

window.helloComponent.alertMessage();

2
In effetti, semplice e funzionale! Come dovrebbe essere. Sono molto colpito da quanto sia semplice; davvero non ci ho pensato. Probabilmente questo approccio non funzionerà così bene se ci sono sempre più componenti che dovresti. Grazie!
Leonardo Maffei

6
L'aggiunta di una variabile globale non è una buona soluzione. Maggiori informazioni sul perché le variabili globali sono cattive qui: wiki.c2.com/?GlobalVariablesAreBad
Salvatore Zappalà

4
Grazie per il voto negativo !! Sì, le variabili globali non sono buone, ma questo è un modo per risolvere il tuo problema.
Kushal Jain

1
Questa è esattamente la soluzione semplice e pragmatica di cui avevo bisogno, grazie!
Florent Destremau

2
ha funzionato ma non funzionerà se hai più lo stesso componente sulla stessa pagina.
gaurav

26

Ho fatto qualcosa di simile:

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

}

E poi da qualche altra parte:

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

}

Non ho testato questo codice esatto, ma è sulla falsariga di quello che ho fatto in un mio progetto e funziona bene :). Ovviamente questo è un cattivo esempio, dovresti usare solo props per questo, ma nel mio caso il sottocomponente ha fatto una chiamata API che volevo mantenere all'interno di quel componente. In tal caso questa è una bella soluzione.


5
Penso che volevi direthis.cowCallbacks.say('moo')
Steven

BTW è possibile passare thisa callback (che sarà un'istanza di Cow) invece di callbacks.
WebBrother

@WebBrother Sì, ma sarebbe ancora più hacky
gitaarik

6

Con il rendermetodo che potenzialmente depreca il valore restituito, l'approccio consigliato è ora quello di allegare un riferimento di richiamata all'elemento radice. Come questo:

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

a cui possiamo quindi accedere utilizzando window.helloComponent, e qualsiasi dei suoi metodi può essere raggiunto con window.helloComponent.METHOD.

Ecco un esempio completo:

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>


Meglio mettere il riferimento nello stato locale invece che sull'oggetto finestra ... ref={component => {this.setState({ helloComponent: component; })}} Quindi, nel gestore dei clic ... this.state.helloComponent.alertMessage();
Bill Dagg

In seguito al mio commento precedente ... o, meglio ancora, metti in stato il metodo alertMessage del componente.
Bill Dagg

Quando provo questo ottengo Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?e non lega il componente alla finestra
Guerrilla

4
class AppProvider extends Component {
  constructor() {
    super();

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

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

Puoi chiamare questo metodo dalla finestra usando window.alertMessage().


Questo funzionerebbe ma l'aggiunta di una variabile globale non è una buona soluzione, per molte ragioni. Puoi trovare maggiori informazioni sul perché le variabili globali sono cattive qui: wiki.c2.com/?GlobalVariablesAreBad
Salvatore Zappalà

3

Se sei in ES6 usa la parola chiave "statica" sul tuo metodo dal tuo esempio sarebbe il seguente: static alertMessage: function() { ...
},

La speranza può aiutare chiunque là fuori :)


Non è possibile raggiungere oggetti di scena o stato in una funzione statica.
Serdar Değirmenci

Ok sì, ma la domanda stava per accedere alla funzione alertMessage () in modo da poter usare HelloElement.alertMessage ().
darmis

Generalmente chiamare una funzione senza usare oggetti di scena e stato non ha alcun effetto. Ma poiché hai ragione riguardo al raggiungimento della funzione, rimuovo il mio voto
Serdar Değirmenci

3

Con 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>
    </>
  )
}

In bocca al lupo...


2

Puoi semplicemente aggiungere un onClickgestore al div con la funzione ( onClickè l'implementazione di React onClick) e puoi accedere alla proprietà tra { }parentesi graffe e apparirà il tuo messaggio di avviso.

Nel caso in cui desideri definire metodi statici che possono essere chiamati sulla classe del componente, dovresti usare statics. Sebbene:

"I metodi definiti all'interno di questo blocco sono statici, il che significa che è possibile eseguirli prima che venga creata qualsiasi istanza di componente e i metodi non hanno accesso agli oggetti di scena o allo stato dei componenti. Se si desidera controllare il valore degli oggetti di scena in una statica metodo, chiedi al chiamante di passare gli oggetti di scena come argomento del metodo statico. " ( fonte )

Qualche codice di esempio:

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

Spero che questo ti aiuti un po ', perché non so se ho capito correttamente la tua domanda, quindi correggimi se l'ho interpretata male :)



2

metodo 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>

Metodo 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>

il metodo 1 è un modo molto semplice e pulito per accedere ai metodi dei componenti figlio. Grazie!
gabdara

1

Uso questo metodo di supporto per eseguire il rendering dei componenti e restituire un'istanza del componente. I metodi possono essere chiamati su quell'istanza.

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);
        });
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.