Reputation: 18694
I am getting data from a WebAPI call in my componentDidMount on my parent react component. I put the values into state.
When I render my form, I am just making custom labels with data (A component), and passing the label text, and the data to this component. (One for each field I am displaying). I pass the values from state, via props, to the child components.
But what I am finding is that my child components are rendering without data being populated... as this seems to happen before the api call happens. The api happens, the state gets set, but data never gets to the child components. I thought that the props would pass the updated state data to the components. I'm wrong. How should I achieve this? I want to load the data in my parent, and then render the child components, passing in the data.
componentDidMount() {
this.loadData();
}
loadData() {
var request = {
method: 'GET',
URL: "http://example.com/api/user/profile",
}
fetchData(request).then(response => {
if(response.errorcode != "OK")
{
console.log("Bad response from API. Need to redirect!")
}
else
{
this.setState(
{
firstname: response.payload.firstname,
surname: response.payload.surname,
email: response.payload.email,
countryId: response.payload.countryId,
countries: response.payload.countries
}
);
}
});
}
render() {
return (
<div>
<h2>Your Profile</h2>
<div className="row">
<div className="col-xs-6">
<DisplayLabel labelText={"Firstname"} data={this.state.firstname} />
<DisplayLabel labelText={"Surname"} data={this.state.surname} />
<DisplayLabel labelText={"Email Address"} data={this.state.email} />
<DisplayLabel labelText={"Country"} data={this.state.countryId} />
<div className="form-group row right">
<button className="btn btn-primary" onClick={this.openModal}>Edit</button>
</div>
</div>
</div>
<Modal isOpen={this.state.modalIsOpen} onAfterOpen={this.afterOpenModal} style={modalStyle}>
<ProfileEditBox closeMeCallback = {this.closeModal} />
</Modal>
</div>
)
}
My display label component is simply this. I'm new to this, and just trying to make reusable components:
import React, {Component} from 'react';
export default class DisplayLabel extends Component {
constructor(props)
{
super(props);
this.state = {
labelText: this.props.labelText,
data: this.props.data
}
console.log(this.state);
}
componentDidMount() {
this.setState({
labelText: this.props.labelText,
data: this.props.data
});
console.log("ComponentDidMount", this.state);
}
render() {
return (
<div>
<div className="row">
<div className="col-xs-12">
<label className="control-label">{this.state.labelText}</label>
</div>
</div>
<div className="row">
<div className="col-xs-12">
<strong><span>{this.state.data}</span></strong>
</div>
</div>
</div>
)
}
}
I need to wait for the API call to complete before I render the form?
Upvotes: 2
Views: 242
Reputation: 1884
Just because constructor called once when component mount so you can do one thing, in your render take two var and assign data into that. And then use those variable to show data. Because child component will render Everytime state of parent will change but constructor will not.
Upvotes: 1
Reputation: 13623
This is a common problem in React. I usually try to resolve it with a pattern that shows some sort of loading indicator. So I would initialize your state
like this in the constructor:
this.state = {
loading: true
}
And I would change your render
to have a check for that loading
bool:
render() {
if (this.state.loading) {
return (<h1>Loading, please wait...</h1>);
}
return (
<div>
<h2>Your Profile</h2>
<div className="row">
<div className="col-xs-6">
<DisplayLabel labelText={"Firstname"} data={this.state.firstname} />
<DisplayLabel labelText={"Surname"} data={this.state.surname} />
<DisplayLabel labelText={"Email Address"} data={this.state.email} />
<DisplayLabel labelText={"Country"} data={this.state.countryId} />
<div className="form-group row right">
<button className="btn btn-primary" onClick={this.openModal}>Edit</button>
</div>
</div>
</div>
<Modal isOpen={this.state.modalIsOpen} onAfterOpen={this.afterOpenModal} style={modalStyle}>
<ProfileEditBox closeMeCallback = {this.closeModal} />
</Modal>
</div>
)
}
Then you can set loading
to false
following a successful data pull, and your form will display correctly without any errors.
(I edited this to use your parent component rather than DisplayLabel
, as it makes more sense there. However, the component name is omitted from your question).
Upvotes: 3
Reputation: 625
I'm not sure you have to wait until the request complete (but you most likely do have to for UI issues), but i guess your problem is something else.
you took the props and included them inside your state (that's unneccery), and then when the component complete it's loading you update the state again, but the request probbaly compleate after the componnent complete it's loading.
if you must to put the props inside your state, you should add componentDidUpdate or componentWillReceiveNewProps lifecycle functions, not the componentDidMount.
but, if you don't have to (most likley), you might change your render funcion to:
render() {
return (
<div>
<div className="row">
<div className="col-xs-12">
<label className="control-label">{this.props.labelText}</label>
</div>
</div>
<div className="row">
<div className="col-xs-12">
<strong><span>{this.props.data}</span></strong>
</div>
</div>
</div>
)
}
(just changed the state to props)
Try that, Good Luck!
Upvotes: 1