¿Cómo acceder a los métodos de componentes desde "afuera" en ReactJS?


183

¿Por qué no puedo acceder a los métodos de componentes desde "afuera" en ReactJS? ¿Por qué no es posible y hay alguna forma de resolverlo?

Considera el código:

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

Tal vez lo necesitas Pubsub?
slideshowp2

Respuestas:


203

React proporciona una interfaz para lo que está intentando hacer a través del refatributo . Asigne un componente a ref, y su currentatributo será su componente personalizado:

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

Nota : Esto solo funcionará si el componente hijo se declara como una clase, según la documentación que se encuentra aquí: https://facebook.github.io/react/docs/refs-and-the-dom.html#adding-a- componente de referencia a clase

Actualización 2019-04-01: Se modificó el ejemplo para usar una clase y los createRefúltimos documentos de React

Actualización 2016-09-19: Se modificó el ejemplo para usar la devolución de llamada de referencia por guía de losref documentos del atributo String .


Entonces, ¿la única forma de comunicarse entre dos componentes secundarios sería tener ambos referencias y pasar por un método proxy en el padre común?
elQueFaltaba

15
React fomenta los componentes basados ​​en datos. Deje que un niño llame a una devolución de llamada que cambia los datos en su antecesor, y cuando esos datos cambien, el otro niño se propsvolverá a generar de manera apropiada.
Ross Allen

@RossAllen, jaja sí, en ese caso también habría tenido que eliminar el punto y coma.
HussienK

@HussienK Prefiero usar un bloque si la función no debe tener un valor de retorno, por lo que la intención es obvia para el próximo desarrollador que lea el código. Cambiar eso a {(child) => this._child = child}crearía una Función que siempre regresa true, pero ese valor no es usado por el refatributo React .
Ross Allen

39

Si desea llamar a funciones en componentes desde fuera de React, puede llamarlas al valor de retorno de renderComponent:

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

La única forma de obtener un identificador para una instancia de React Component fuera de React es almacenando el valor de retorno de React.renderComponent. Fuente .


1
en realidad funciona para reaccionar16. El método de representación ReactDOM devuelve una referencia al componente (o devuelve nulo para componentes sin estado).
Vlad Povalii

37

Alternativamente, si el método en Child es verdaderamente estático (no es un producto de accesorios actuales, estado), puede definirlo staticsy luego acceder a él como lo haría con un método de clase estática. Por ejemplo:

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

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

1
La fuente de esto está aquí .
tirdadc

7

A partir de React 16.3 React.createRefse puede usar (usar ref.currentpara acceder)

var ref = React.createRef()

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

React.renderComponent(parent, document.body)

4

Desde React 0.12, la API ha cambiado ligeramente . El código válido para inicializar myChild sería el siguiente:

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

1

También podría hacerlo así, no estoy seguro si es un buen plan: D

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

Como se menciona en algunos de los comentarios, ReactDOM.renderya no devuelve la instancia del componente. Puede pasar una refdevolución de llamada al representar la raíz del componente para obtener la instancia, de esta manera:

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

y:

// vanilla javascript code
var global_widget_instance;

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

global_widget_instance.myCoolMethod();

-1

Otra forma tan fácil:

funcionar afuera:

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

Atarlo:

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

Vea el tutorial completo aquí: ¿Cómo usar "esto" de un Componente React desde afuera?

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.