Comment accéder aux méthodes des composants depuis «l'extérieur» dans ReactJS?


183

Pourquoi ne puis-je pas accéder aux méthodes des composants de «l'extérieur» dans ReactJS? Pourquoi n'est-ce pas possible et y a-t-il un moyen de le résoudre?

Considérez le code:

var Parent = React.createClass({
    render: function() {
        var child = <Child />;
        return (
            <div>
                {child.someMethod()} // expect "bar", got a "not a function" error.
            </div>
        );
    }
});

var Child = React.createClass({
    render: function() {
        return (
            <div>
                foo
            </div>
        );
    },
    someMethod: function() {
        return 'bar';
    }
});

React.renderComponent(<Parent />, document.body);

Vous en avez peut-être besoin Pubsub?
diaporamap2

Réponses:


203

React fournit une interface pour ce que vous essayez de faire via l' refattribut . Attribuez un composant a ref, et son currentattribut sera votre composant personnalisé:

class Parent extends React.Class {
    constructor(props) {
        this._child = React.createRef();
    }

    componentDidMount() {
        console.log(this._child.current.someMethod()); // Prints 'bar'
    }

    render() {
        return (
            <div>
                <Child ref={this._child} />
            </div>
        );
    }
}

Remarque : cela ne fonctionnera que si le composant enfant est déclaré en tant que classe, selon la documentation trouvée ici: https://facebook.github.io/react/docs/refs-and-the-dom.html#adding-a- ref-to-a-class-component

Mise à jour 2019-04-01: exemple modifié pour utiliser une classe et createRefselon les derniers documents React.

Mise à jour 19/09/2016: exemple modifié pour utiliser le rappel de référence conformément aux instructions de laref documentation sur l' attribut String .


Donc, le seul moyen de communiquer entre deux composants enfants serait à la fois d'avoir des références et de passer par une méthode proxy sur le parent commun?
elQueFaltaba

15
React encourage les composants basés sur les données. Laissez un enfant appeler un rappel qui modifie les données de son ancêtre, et lorsque ces données changent, l'autre enfant sera nouveau propset restitué de manière appropriée.
Ross Allen

@RossAllen, haha ​​oui, vous auriez dû supprimer le point-virgule aussi dans ce cas.
HussienK

@HussienK Je préfère utiliser un bloc si la fonction ne doit avoir aucune valeur de retour, donc l'intention est évidente pour le prochain développeur qui lit le code. Changer cela pour {(child) => this._child = child}créerait une fonction qui retournait toujours true, mais cette valeur n'est pas utilisée par l' refattribut de React .
Ross Allen le

39

Si vous souhaitez appeler des fonctions sur des composants depuis l'extérieur de React, vous pouvez les appeler sur la valeur de retour de renderComponent:

var Child = React.createClass({…});
var myChild = React.renderComponent(Child);
myChild.someMethod();

La seule façon d'obtenir un handle vers une instance de React Component en dehors de React est de stocker la valeur de retour de React.renderComponent. Source .


1
en fait cela fonctionne pour react16. La méthode de rendu ReactDOM renvoie une référence au composant (ou renvoie null pour les composants sans état).
Vlad Povalii

37

Alternativement, si la méthode sur Child est vraiment statique (pas un produit des accessoires actuels, de l'état), vous pouvez la définir sur statics, puis y accéder comme vous le feriez pour une méthode de classe statique. Par exemple:

var Child = React.createClass({
  statics: {
    someMethod: function() {
      return 'bar';
    }
  },
  // ...
});

console.log(Child.someMethod()) // bar

1
La source pour cela est ici .
tirdadc

7

À partir de React 16.3 React.createRefpeut être utilisé, (utiliser ref.currentpour accéder)

var ref = React.createRef()

var parent = <div><Child ref={ref} /> <button onClick={e=>console.log(ref.current)}</div>

React.renderComponent(parent, document.body)

4

Depuis React 0.12, l'API a été légèrement modifiée . Le code valide pour initialiser myChild serait le suivant:

var Child = React.createClass({…});
var myChild = React.render(React.createElement(Child, {}), mountNode);
myChild.someMethod();

1

Vous pouvez aussi le faire comme ceci, sans savoir si c'est un bon plan:

class Parent extends Component {
  handleClick() {
    if (this._getAlert !== null) {
      this._getAlert()
    }
  }

  render() {
    return (
      <div>
        <Child>
        {(getAlert, childScope) => (
          <span> {!this._getAlert ? this._getAlert = getAlert.bind(childScope) : null}</span>
        )}
        </Child>
        <button onClick={() => this.handleClick()}> Click me</button>
      </div>
      );
    }
  }

class Child extends Component {
  constructor() {
    super();
    this.state = { count: 0 }
  }

  getAlert() {
    alert(`Child function called state: ${this.state.count}`);
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return this.props.children(this.getAlert, this)
  }
}

1

Comme mentionné dans certains des commentaires, ReactDOM.renderne renvoie plus l'instance du composant. Vous pouvez passer un refrappel lors du rendu de la racine du composant pour obtenir l'instance, comme ceci:

// React code (jsx)
function MyWidget(el, refCb) {
    ReactDOM.render(<MyComponent ref={refCb} />, el);
}
export default MyWidget;

et:

// vanilla javascript code
var global_widget_instance;

MyApp.MyWidget(document.getElementById('my_container'), function(widget) {
    global_widget_instance = widget;
});

global_widget_instance.myCoolMethod();

-1

Une autre façon si simple:

fonction à l'extérieur:

function funx(functionEvents, params) {
  console.log("events of funx function: ", functionEvents);
  console.log("this of component: ", this);
  console.log("params: ", params);
  thisFunction.persist();
}

Liez-le:

constructor(props) {
   super(props);
    this.state = {};
    this.funxBinded = funx.bind(this);
  }
}

Veuillez consulter le tutoriel complet ici: Comment utiliser "this" d'un composant React de l'extérieur?

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.