2

Right, that's a hell of a name. But I just can't in my state come up with a better title.

The issue is the following.

In React, during componentWillMount I initiate the state to null, which then gets updated after data is pulled from a server via ajax.

The structure looks like this:

  componentWillMount() {
    this.state = { calculator: {} };

  initCalculator()
    .then(calculator => {
      this.setState({ calculator });
    })
    .catch(err => {
      console.log(err);
    });
  }

The data itself is pulled here:

  function initCalculator() {
    const promise = new Promise((resolve, reject) => {
      const instance = parseInstance();
      const compId = Wix.Utils.getOrigCompId();

      axios.get(`/api/calculator?instance=${instance}&compId=${compId}`)
        .then(res => {
          resolve(res.data);
        })
        .catch(err => {
          reject(err);
        });
    });

    return promise;
  }

res.data is the response object with the data from the server.

This data then gets passed to state as you've seen above.

This state data then gets passed to another element as so:

<Settings tab="Settings" calculator={ this.state.calculator } onUpdate={ this.settingsUpdate.bind(this) }/>

The data, however, is not "resolved" on the other end of the Settings tab.

whenever I try to access properties deep down in the calculator object, it always throws an error at me.

cannot read property settings of null

This can be seen here

const name = this.props.calculator.settings ? this.props.calculator.settings.name : '';

So, if I tried to access calculator.settings.name, it would throw a similar error at me.

Also, the const name, always gets set to '', even during second re-render, when presumably the data should already be available, because the state has been updated, thus prompting a re-render in the child element.

I understand why this happens. Somewhat at least. The object is passed before it is resolved, so during the second state change, which is this.setState({ calculator }); the re-render gets called before the state is fully updated. At least I believe so.

If I console.log(this.props.calculator), I can see all that data just fine. But because the console.log, upon clicking the arrow to expand the details, it resolves the data in the mean time.

If I do JSON.stringify(this.props.calculator), the data is all shown and available there. Now I know that I can probably get around this using JSON.stringify, and JSON.parse. But that's just a dumb way to solve such an issues that I've likely created myself.

I understand that objects work through references. So I reckon that the issue lies somewhere in the initial ajax get request, or the setState method.

Perhaps someone with a keen eye would be able to see what the issue is here. But at this point I'm just banging my head on the wall.

Dom Berk
  • 111
  • 9
  • After some more testing, I can conclude that it really is `this.setState(...);` that causes the problem. It doesn't update the state fast enough before re-rendering the child component. Thus the child receives still the old copy of the state. Haven't come up with a solution yet. – Dom Berk Nov 09 '16 at 18:59
  • Alright, so the solution seems to be the fact that in order to update and re-render an element on state update, in some cases you need to use `key` property on the component. I will further test it, and submit an answer to my own question tomorrow. – Dom Berk Nov 09 '16 at 19:38
  • Avoid the [`Promise` constructor antipattern](http://stackoverflow.com/q/23803743/1048572)! – Bergi Nov 10 '16 at 02:17
  • Thanks, yeah, I could have just returned the original `axios.get(...)` and that would function the same. – Dom Berk Nov 10 '16 at 06:18

1 Answers1

0

Set your state in your constructor rather than your componentWillMount. Make your ajax call on componentDidMount, and update your state on successfull request.

     constructor(props) {
       super(props);
       this.state = {
        data: null,
       }
     }

     componentWillMount() {

     }

     updataStateFromCompletedAjax(data) {
      this.setState({
       data: data
      })
     }
Djtouchette
  • 395
  • 2
  • 6
  • Any real difference between calling an async function in `componentDidMount` rather than `componentWillMount` ? Or it's rather just better practice in general? – Dom Berk Nov 10 '16 at 08:09
  • Honestly its mostly best practice, but this question breaks down the difference. http://stackoverflow.com/questions/29899116/what-is-the-difference-between-componentwillmount-and-componentdidmount-in-react – Djtouchette Nov 11 '16 at 00:26