Reputation: 575
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
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