I am building an app where I get "complaints" from my Nodejs app. Then I pass those complaints to child component as props. In child component, I set those props to state in ComponentDidMount()
. But render()
function gets called first so it does not show anything on screen. State is not set immediately. please help. I am struggling from 2 days.
this is parent component
class Complainer extends Component {
state = {
complaints: []
};
async componentDidMount() {
try {
const user = auth.getCurrentUser();
console.log(user);
this.setState({ user });
if (!user || user.role !== 'complainer') this.props.history.replace('/');
} catch (ex) {
window.location = '/login';
}
const { data: complaints } = await getComplaints();
this.setState({ complaints });
}
render() {
const { complaints } = this.state;
return (
<React.Fragment>
<Navbar user={this.state.user} />
<div className="container">
<Switch>
<Route
path="/complainer/view-all"
render={props => (
<AllComplaints complaints={complaints} {...props} />
)}
/>
<Route path="/complainer/new-complaint" component={ComplaintForm} />
<Route path="/complainer/not-found" component={notfound} />
<Showcase user={this.state.user} />
</Switch>
<hr />
</div>
</React.Fragment>
);
}
}
export default Complainer;
This is child
class AllComplaints extends Component {
state = {
data: {
columns: [
{
label: 'location',
field: 'location',
sort: 'asc',
width: 280
},
{
label: 'Title',
field: 'title',
sort: 'asc',
width: 150
}
],
rows: []
}
};
componentDidMount() {
const { complaints } = this.props;
this.setState({ rows: complaints });
}
render() {
const { data } = this.state;
return (
<React.Fragment>
{data.rows && <MDBDataTable striped bordered small data={data} />}
{!data.rows && <h1>There is no complaints to show</h1>}
</React.Fragment>
);
}
}
export default AllComplaints;
The problem is, as in state, rows are empty .i.e. []. in componentDidMount()
I am setting the coming props to state of this "AllComplaints" component.
I want to set the "props" value to "this.state.data.rows". But this is not happening. It takes time to set but render()
is called first so it shows nothing on the screen.
Please help.
Two things:
As @karahan mentioned, you're not setting your state correctly.
this.setState({ rows: complaints });
should be something like:
this.setState(prevState => { data: { ...prevState.data, rows: complaints } });
This probably won't be sufficient however. You're retrieving your data in componentDidMount
of the parent, and setting your state in componentDidMount
of the child. However, the componentDidMount
of the child is always run first if they're mounted at the same time, and is only run once. This means that with your setup, your child's props.complaints
has the potential to be empty in componentDidMount
.
I'd solve this by not using state in your child's component. There's rarely a good reason to store the same state in multiple components (in this case, you're storing the same thing in both the parent and the child). Just use your props, which will always be up-to-date in your render function. Your rendering of MDBDataTable
should probably look something like this:
<MDBDataTable striped bordered small data={{ ...this.state.data, rows: this.props.complaints }} />
If you absolutely must store complaints
in the state of the child for some reason, you should either fetch the data in the child instead, or look into getDerivedStateFromProps
and componentDidUpdate
. However, its unlikely that you need such a setup.