How to Pass, Access & Update Parent State from Child Components in React
in a previous tutorial, we've built a simple React application with a parent App
component and child ContactForm
component. The parent component displays an HTML table of data which gets retrieved from a PHP API backend and stored in a state variable named contacts
. This is the example ContactForm
child component:
class ContactForm extends React.Component {
state = {
name: '',
email: '',
country: '',
city: '',
job: '',
}
handleFormSubmit( event ) {
event.preventDefault();
let formData = new FormData();
formData.append('name', this.state.name)
formData.append('email', this.state.email)
formData.append('city', this.state.city)
formData.append('country', this.state.country)
formData.append('job', this.state.job)
axios({
method: 'post',
url: '/api/contacts.php',
data: formData,
config: { headers: {'Content-Type': 'multipart/form-data' }}
})
.then(function (response) {
//handle success
console.log(response)
})
.catch(function (response) {
//handle error
console.log(response)
});
}
render(){
return (
<form>
<label>Name</label>
<input type="text" name="name" value={this.state.name}
onChange={e => this.setState({ name: e.target.value })}/>
<label>Email</label>
<input type="email" name="email" value={this.state.email}
onChange={e => this.setState({ email: e.target.value })}/>
<label>Country</label>
<input type="text" name="country" value={this.state.country}
onChange={e => this.setState({ country: e.target.value })}/>
<label>City</label>
<input type="text" name="city" value={this.state.city}
onChange={e => this.setState({ city: e.target.value })}/>
<label>Job</label>
<input type="text" name="job" value={this.state.job}
onChange={e => this.setState({ job: e.target.value })}/>
<input type="submit" onClick={e => this.handleFormSubmit(e)} value="Create Contact" />
</form>);
}
}
The child component renders an HTML form and stores its internal state comprised of the values of the form fields. It also provides a handleFormSubmit()
method which handles submitting the form to the backend REST API using the Axios client and the FormData
structure.
This is the implementation of the parent App
component:
class App extends React.Component {
state = {
contacts: []
}
componentDidMount() {
const url = '/api/contacts.php'
axios.get(url).then(response => response.data)
.then((data) => {
this.setState({ contacts: data })
console.log(this.state.contacts)
})
}
render() {
return (
<React.Fragment>
<h1>Contact Management</h1>
<table border='1' width='100%' >
<tr>
<th>Name</th>
<th>Email</th>
<th>Country</th>
<th>City</th>
<th>Job</th>
</tr>
{this.state.contacts.map((contact) => (
<tr>
<td>{ contact.name }</td>
<td>{ contact.email }</td>
<td>{ contact.country }</td>
<td>{ contact.city }</td>
<td>{ contact.job }</td>
</tr>
))}
</table>
<ContactForm />
</React.Fragment>
);
}
}
The App
component has a contacts
state variable that will be used to hold data retrieved from a REST API with Axios.
In the componentDidMount()
method we send a call to the API and use the React setState()
method to update the component state with the fetched data.
The render()
method returns a React fragment and displays an HTML table of contacts data and the ContactForm
component.
ContactForm
is a child component of App
.
We create new contacts in the ContactForm
component and we want the new contact to be added to the HTML table (i.e to the parent App
contacts
state) without the need to refresh the table.
This means we need to access the contacts
state of the parent App
component from the ContactForm
child component so we can push the new contact to the contacts
array in the handleFormSubmit()
method of the child component when the Axios Promise is successfully resolved (i.e the contact is successfully created in the server).
How to Access the Parent Component State from The Child Component in React
We can access the state of the parent React component using various methods such as props and the context API:
Sending the Parent State as A Prop of Child Component
You can send the state of the parent component as a prop to the child component:
<ContactForm contacts={this.state.contacts} />
In the parent component you can get the passed state as follows:
this.props.contacts
Since props are immutables, you can't update the parent state using this method.
Using a Prop Method to Handle State Update
To be able to access and update state from the child component, we can add a method that handles updating the state to the parent component and pass the method as a prop to the child component instead of the state itself.
So, let's implement this step by step in our previous example. In the parent App
component, add the following handleStateChange()
method and bind it to the class:
class App extends React.Component {
constructor () {
super();
this.handleStateChange = this.handleStateChange.bind(this);
}
state = {
contacts: []
}
handleStateChange(value){
event.preventDefault();
let contacts = this.state.contacts;
contacts.push(value);
this.setState({ contacts : contacts })
}
In the handleStateChange()
method we grab the contacts
state variable, we push the contact passed as a parameter and we use the React setState()
method to update the state.
Next, pass the method as a prop to the child component:
<ContactForm handleStateChange = {this.handleStateChange} />
Next, call the handleStateChange()
method in the handleFormSubmit()
method of ContactForm
and pass the new contact as a parameter:
handleFormSubmit( event ) {
event.preventDefault();
let formData = new FormData();
formData.append('name', this.state.name)
formData.append('email', this.state.email)
formData.append('city', this.state.city)
formData.append('country', this.state.country)
formData.append('job', this.state.job)
var contact = {};
formData.forEach(function(value, key){
contact[key] = value;
});
this.props.handleStateChange(contact);
/* [...] */
}
Use React Context API
Instead of props, you can also use the Contexts API.
Using Redux for Accessing and Updating Parent State from Child Component
If your application is more complex than this simple example, you can use a state management library like Redux and connect()
both the App
and ContactForm
components to the contacts
in Redux store. In this case, the contacts
state variable needs to be part of global app state.
Conclusion
In this tutorial, we've seen how to access and update the state of a parent component from a child component.
-
Date: