TOTΣM
TOTΣM

Reputation: 59

How to use a callback in a render() of React component

I want to use the result of a server method that does a count on a collection to show it in the render method of a react component. I understand that I must do it from the callback function but it doesn't work for me.

Server Method:

export const analysis = new ValidatedMethod({
  name: "analysis",
  validate: new SimpleSchema({
    codigo: {
      type: String
    },
    rta: {
      type: String
    }
  }).validator(),
  run(one) {
    const rta = Respuesta.find({
      codigo: one.codigo,
      rtatexto: one.rta,
      activo: true
    }).count();
    console.log(rta);
    return Number(rta);
  }
});

Call from client:

export default class AnalysisFila extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    const one = { codigo: this.props.codigo, rta: this.props.opcion };
    const rta = analysis.call(one, (err, res) => {
      return (
        <Table.Row>
          <Table.Cell> {this.props.opcion} </Table.Cell>
          <Table.Cell> {res} </Table.Cell>
        </Table.Row>
      );
    });
  }
}

How do I use the value of res in my component's render method?

Upvotes: -1

Views: 5659

Answers (1)

David Losert
David Losert

Reputation: 4802

Asynchronous Functions

Before I answer the question, it is important to understand the difference between synchronous and asynchronous functions in JavaScript.

Therefore, if you are new to JavaScript or asynchronous behaviours, I recommend you read up on this excellent answer and explanation about the topic

Explanation for the Problem

The problem here is that React's render() method is a synchronous method, meaning React expects a synchronous return value with jsx (or a React.Element for that matter).

For React, your current render function actually returns nothing or undefined, as you do not a have a synchronous return statement. And so nothing will get rendered.

React has no way of knowing when your callback is called or what it returns, as it is executed completely out of Reacts context.

Solution to the Problem

The way to go here is to use Reacts state which is exactly for these kind of scenarios.

By using state and the method setState(), you can asynchrounously trigger the render-method to be called again as soon as the response from your API delivered the data. Also, with this.state, you can bring the response-data into the react-context and let it know about it.

A full example could look like this:

export default class AnalysisFila extends Component {
  constructor(props) {
    super(props);
    this.state = {
      loaded: false
      opcion: {}
    };
  }

  componentDidMount() {
    const one = { codigo: this.props.codigo, rta: this.props.opcion };
    const rta = analysis.call(one, (err, res) => {
      this.setState({
        loaded: true,
        opcion: res
      }
    });
  }

  render() {
    const content = this.state.loaded ? this.state.opcion : 'Loading...';
    return (
     <Table.Row>
        <Table.Cell> {this.props.opcion} </Table.Cell>
        <Table.Cell> {content} </Table.Cell>
      </Table.Row>
    );
  } 
}

I also used the componentDidMount-Lifecycle-Method of React, as it is the recommended way to trigger API Calls. You can read more about Lifecycles in the official lifecycle documentation

The Life-Cycle of this Component will be like the following:

  1. Component will get created and constructor will set an initial value for this.state
  2. render() method is called with this.state.loading === false - thus the content will default to the string 'loading...'
  3. The componentDidMount() function gets called and will trigger your API-Call
  4. Your callback is called and executes setState(), putting the response-data into this.state
  5. Now that react knows the state was changed, it calls the render() method again. But this time, this.state.loading === true and there is some value for this.state.opcion - thus the content will be the value from your response.

Upvotes: 1

Related Questions