aver
aver

Reputation: 575

reactjs storing state in constructor vs property

I have a very simple react component that needs to connect to an API and retrieve some JSON data, which will then be used for displaying some information.

In the following class/component, I have mounted and state as a property. I normally use a constructor to hold my states but in this case, if I move the state to a constructor, I cannot seem to access the data (projectInfo) inside the renderer. When inside the renderer (line containinig {projectInfo.name}), I get the error: TypeError: Cannot read property 'name' of null

How can I use the constructor in this class to hold the state? Why does the following class work but not when I use a constructor? What is the convention for handling something like this?

class MyReportSummary extends Component {
  mounted = true;
  state = {
    projectInfo: null,
    isLoading: true,
    error: null
  };

  componentDidMount() {
    fetch(`/api/projects/${this.props.projectId}`)
      .then(response => {
        if (response.ok) {
          return response.json();
        } else {
          throw new Error("Encountered problem fetching project info");
        }
      })
      .then(data => {
        if (this.mounted) {
          this.setState({
            projectInfo: data
          });
        }
      })
      .catch(fetchError => {
        if (this.mounted) {
          this.setState({
            isLoading: false,
            error: fetchError
          });
        }
      });
  }

  componentWillUnmount() {
    this.mounted = false;
  }

  render() {
    const { isLoading, error, projectInfo } = this.state;

    if (error) {
      return <p>{error.message}</p>;
    }

    if (isLoading) {
      return <p>Loading...</p>;
    }

    return (
      <div className="myReportSummary">
        Summary of Project name: {projectInfo.name}
        Number of events: {this.props.data.length}
      </div>
    );
  }
}

UPDATE: Just for clarity, the above sample code works just fine. What I'm trying to understand is if my class look like this that has a constructor initializing state, then I get that TypeError.

class MyReportSummary extends Component {
  mounted = true;

  constructor(props) {
    super(props);

    this.state = {
        projectInfo: null,
        isLoading: false,
        error: null
    };
  }

  componentDidMount() {
    // same as the previous sample code
  }

  componentWillUnmount() {
    this.mounted = false;
  }

  render() {
    //same as the previous sample code
  }
}

What is the correct convention for states? Is constructor not the proper way of doing this?

Upvotes: 1

Views: 610

Answers (1)

Tholle
Tholle

Reputation: 112787

Initializing state in the constructor like your second example is perfectly valid, but your two examples are not the same. You are setting isLoading to true in the class property version, but isLoading to false in the constructor.

If error in null and isLoading is false you will hit the last part of your render method. Since projectInfo is null before your request is complete, you will try to access name on null and get your error.

Set isLoading to true and it should work as expected, or even projectInfo to an empty object {}, but then you will not get the loading indicator.

class MyReportSummary extends Component {
  constructor(props) {
    super(props);

    this.mounted = true;
    this.state = {
      projectInfo: {},
      isLoading: true,
      error: null
    };
  }

  // ...
}

Upvotes: 1

Related Questions