jsbisht
jsbisht

Reputation: 9649

How does ES6 class instance variable work

I have the following code which is working as expected:

import React, { Component } from 'react';

let result = null;
class MyData extends Component {
  _getData = () => {
    fetch(url)
      .then(response => response.json())
      .then(data => {
        result = items.find(
          item => item.id === 'abcd'
        );
      });
  };

  componentDidMount() {
    this._getData();
  }

  render() {
    return result ? <span>hello</span> : null;
  }
}

When I try to move result as an instance variable of the class as follows, it always remains null in the render() method. Though the assignment is working correctly post fetch:

import React, { Component } from 'react';

class MyData extends Component {
  result = null;
  _getData = () => {
    fetch(url)
      .then(response => response.json())
      .then(data => {
        this.result = items.find(
          item => item.id === 'abcd'
        );
      });
  };

  componentDidMount() {
    this._getData();
  }

  render() {
    return this.result ? <span>hello</span> : null;
  }
}

Can someone explain me what is the expected usage here. And how things have changed from ES6 onwards.

Upvotes: 1

Views: 195

Answers (3)

Nitsew
Nitsew

Reputation: 3662

The reason you are seeing null is because the state is not being updated in the code example you posted. The component mounts and this.result is null. But when the _getData method is called and the response comes back, the component is not being rendered.

You should be storing this data in your state, as React relies on the state changes to know when it should re-render the component. Try something like this:

import React, { Component } from 'react';

class MyData extends Component {
  state = {
    result: null
  }
  _getData = () => {
    fetch(url)
      .then(response => response.json())
      .then(data => {
        this.setState({
          result: items.find(
            item => item.id === 'abcd'
          )
        })
      });
  };

  componentDidMount() {
    this._getData();
  }

  render() {
    const { result } = this.state
    return result ? <span>hello</span> : null;
  }
}

You can learn more about rendering, state, and component lifecycles here: https://reactjs.org/docs/state-and-lifecycle.html#adding-local-state-to-a-class

Edit: If you want to experiment with the new react hooks api, you could convert your component to look like the folllowing:

import React, { useEffect, useState } from 'react';

const MyData = ({ ...props }) => {
  const [result, setResult] = useState(null);

  const getData = () => {
    fetch(url)
      .then(response => response.json())
      .then(data => {
        const result = items.find(item => item.id === 'abcd');

        setResult(result);
      });
  };

  useEffect(() => {
    getData();
  }, []);

  return result ? <span>hello</span> : null;
}

Upvotes: 1

LukeT
LukeT

Reputation: 340

I think the main thing you're missing is state. Keep in mind that these Components you are extending are React Component classes which have a very specific life cycle. Look into component life cycle for more info. This will work as expected with the following refactor:

import React, { Component } from 'react';

class MyData extends Component {
    constructor() {
        super();
        this.state = {result:  null};
    }

    _getData = () => {
        fetch(url)
        .then(response => response.json())
        .then(data => {
            let resp = data.items.find(
                item => item.id === 'abcd'
            );

            this.setState({ result: resp });
        });
    };

    componentDidMount() {
        this._getData();
    }

    render() {
       return this.state.result ? <span>hello</span> : null;
    }
 }

Upvotes: 0

Dennis Vash
Dennis Vash

Reputation: 53874

Both examples just don't work, the reason is that componentDidMount lifecycle fires after render lifecycle.

Because you don't cause re-render (with setState) in any phase, nothing will cause an additional render to read the updated value of result.

enter image description here

Therefore, the next example will always render No Result

import ReactDOM from 'react-dom';
import React, { Component } from 'react';

let result = null;

class App extends Component {
  thisResult = null;
  setResult = () => {
    result = 10;
    this.thisResult = 10;
  };

  componentDidMount() {
    this.setResult();
  }

  render = () => {
    return <div>{result || this.thisResult ? 'Result' : 'No Result'}</div>;
  };
}

ReactDOM.render(<App />, document.getElementById('root'));

Edit flamboyant-snowflake-u4s6x

Upvotes: 4

Related Questions