Appeler une méthode de composant React de l'extérieur

92

Je souhaite appeler une méthode exposée par un composant React à partir de l'instance d'un élément React.

Par exemple, dans ce fichier jsfiddle . Je veux appeler la alertMessageméthode à partir de la HelloElementréférence.

Existe-t-il un moyen d'y parvenir sans avoir à écrire des wrappers supplémentaires?

Edit (code copié de 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')
);
Guerrier riche
la source
3
Pas idéal, mais JSFiddle est suffisamment courant pour ne pas justifier un vote négatif.
Jeff Fairley
Je me demande quel pourrait être votre cas d'utilisation qui justifierait une telle chose. Ce n'est pas une bonne façon de concevoir votre application imo. Si vous avez besoin de réutiliser quelque chose, veuillez créer un assistant commun séparé dans un troisième fichier et l'utiliser pour votre bouton ainsi que votre composant de réaction.
thé aromatisé le

Réponses:

55

Il existe deux manières d'accéder à une fonction interne. Un niveau d'instance, comme vous le souhaitez, un autre niveau statique.

Exemple

Vous devez appeler la fonction au retour de React.render. Voir ci-dessous.

Statique

Jetez un œil à ReactJS Statics . Notez, cependant, qu'une fonction statique ne peut pas accéder aux données au niveau de l'instance, ce thisserait le cas 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'));

Alors fais HelloRendered.alertMessage().

Jeff Fairley
la source
13
Notez que l'utilisation de la valeur de retour de render est considérée comme obsolète et devrait être supprimée dans les versions futures pour permettre des améliorations de performances. La méthode prise en charge pour obtenir une référence à votre objet d'instance de composant consiste à ajouter une propriété refqui est une fonction appelée avec l'instance en tant que paramètre. Cela vous permet également d'accéder à des objets qui ne sont pas au niveau supérieur, par exemple si vous effectuez un rendu, <MuiThemeProvider><Hello ref={setHelloRef} /></MuiThemeProvider>vous obtenez la bonne référence transmise à votre setHelloReffonction plutôt qu'une à MuiThemeProvider.
Periata Breatta
43

Tu peux faire comme

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;

Maintenant de l'extérieur de ce composant, vous pouvez appeler comme ceci ci-dessous

window.helloComponent.alertMessage();
Kushal Jain
la source
1
En fait, simple et fonctionnel! Comme il est censé être. Je suis très impressionné par sa simplicité; vraiment pas pensé. Cette approche ne fonctionnera probablement pas très bien s'il y a de plus en plus de composants que vous devriez. Je vous remercie!
Leonardo Maffei
6
L'ajout d'une variable globale n'est pas une bonne solution. Plus d'informations sur les raisons pour lesquelles les variables globales sont mauvaises ici: wiki.c2.com/?GlobalVariablesAreBad
Salvatore Zappalà
3
Merci pour votre vote! Oui, les variables globales ne sont pas bonnes, mais c'est un moyen de résoudre votre problème.
Kushal Jain
C'est exactement la solution pragmatique et simple dont j'avais besoin, merci!
Florent Destremau
2
cela a fonctionné mais cela ne fonctionnera pas si vous avez le même composant sur la même page.
gaurav
24

J'ai fait quelque chose comme ça:

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

}

Et puis ailleurs:

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

}

Je n'ai pas testé ce code exact, mais c'est dans le sens de ce que j'ai fait dans un de mes projets et cela fonctionne bien :). Bien sûr, c'est un mauvais exemple, vous devriez simplement utiliser des accessoires pour cela, mais dans mon cas, le sous-composant a fait un appel d'API que je voulais garder à l'intérieur de ce composant. Dans un tel cas, c'est une bonne solution.

gitaarik
la source
5
Je pense que vous vouliez direthis.cowCallbacks.say('moo')
Steven
BTW, il est possible de passer thisà callback (qui sera une instance de Cow) au lieu de callbacks.
WebBrother
@WebBrother Oui, mais ce serait encore plus hacky
gitaarik
6

La renderméthode rendant potentiellement obsolète la valeur renvoyée, l'approche recommandée consiste maintenant à attacher une référence de rappel à l'élément racine. Comme ça:

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

auquel nous pouvons ensuite accéder en utilisant window.helloComponent, et n'importe laquelle de ses méthodes est accessible avec window.helloComponent.METHOD.

Voici un exemple complet:

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>

Brett DeWoody
la source
Mieux vaut mettre la référence dans l'état local plutôt que sur l'objet fenêtre ... ref={component => {this.setState({ helloComponent: component; })}} Ensuite, dans le gestionnaire de clic ... this.state.helloComponent.alertMessage();
Bill Dagg
Suite à mon commentaire précédent ... ou, mieux encore, mettez simplement la méthode alertMessage du composant à l'état.
Bill Dagg
Quand j'essaye ceci, j'obtiens Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?et cela ne lie pas le composant à la fenêtre
Guerrilla
4
class AppProvider extends Component {
  constructor() {
    super();

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

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

Vous pouvez appeler cette méthode depuis la fenêtre en utilisant window.alertMessage().

Mygel Bergstresser
la source
Cela fonctionnerait, mais l'ajout d'une variable globale n'est pas une bonne solution, pour de nombreuses raisons. Vous pouvez trouver plus d'informations sur les raisons pour lesquelles les variables globales sont mauvaises ici: wiki.c2.com/?GlobalVariablesAreBad
Salvatore Zappalà
3

Si vous êtes dans ES6, utilisez simplement le mot-clé "static" sur votre méthode de votre exemple serait le suivant: static alertMessage: function() { ...
},

L'espoir peut aider n'importe qui là-bas :)

Darmis
la source
Vous ne pouvez pas atteindre les accessoires ou l'état dans une fonction statique.
Serdar Değirmenci
Ok oui, mais la question était sur le point d'accéder à la fonction alertMessage () afin que vous puissiez utiliser HelloElement.alertMessage ().
darmis
Généralement, appeler une fonction sans utiliser d'accessoires ni d'état n'a aucun effet. Mais comme vous avez raison d'atteindre la fonction, je supprime mon vote
Serdar Değirmenci
2

Vous pouvez simplement ajouter un onClickgestionnaire au div avec la fonction ( onClickc'est la propre implémentation de React onClick) et vous pouvez accéder à la propriété entre { }accolades, et votre message d'alerte apparaîtra.

Si vous souhaitez définir des méthodes statiques qui peuvent être appelées sur la classe de composant, vous devez utiliser la statique. Bien que:

"Les méthodes définies dans ce bloc sont statiques, ce qui signifie que vous pouvez les exécuter avant la création de toute instance de composant et que les méthodes n'ont pas accès aux accessoires ou à l'état de vos composants. Si vous souhaitez vérifier la valeur des accessoires dans un fichier statique méthode, demandez à l'appelant de transmettre les accessoires en tant qu'argument de la méthode statique. " ( source )

Quelques exemples de code:

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

J'espère que cela vous aidera un peu, car je ne sais pas si j'ai bien compris votre question, alors corrigez-moi si je l'ai mal interprétée :)

Danillo Felixdaal
la source
2

méthode 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>

Méthode 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>
Mohideen bin Mohammed
la source
La méthode 1 est un moyen très simple et propre d'accéder aux méthodes des composants enfants. Merci!
gabdara
2

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

Bonne chance...

Aakash
la source
1

J'utilise cette méthode d'assistance pour rendre les composants et renvoyer une instance de composant. Des méthodes peuvent être appelées sur cette instance.

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);
        });
    }
Stefan
la source