The way that components “talk” to one another in React can at first seem confusing. “Parent to Child” communication is relatively straightforward using the “props” system, but “Child to Parent” communication using event handlers didn’t make sense to me when I first learned how it works, so I decided to make a more easy-to-understand example.
We’ll call App.js the “Parent”
We’ll create two extra components called “Brother.js” and “Sister.js” — These two components are Children of App.js and they are siblings.
Here are some examples of communication —
Let’s first setup App.js so that it is the parent of the two child components:
import Brother from "./components/Brother";
import Sister from "./components/Sister";
function App() {
return (
<div>
<Brother />
<Sister />
</div>
);
}
export default App;
So we’ve imported two components, Brother.js and Sister.js — Because we imported them into App.js this automatically makes App.js the “parent” component.
We have a basic function that simply returns the two components:
return (
<div>
<Brother />
<Sister />
</div>
);
so whatever is actually inthose components, will be returned to the screen. Here is the code for the two child components:
function Brother() {
return (
<div>I am the Brother component, son of the parent!</div>
);
}
export default Brother;
function Sister() {
return (
<div>I am the Sister component, daughter of the parent!</div>
);
}
export default Sister;
We don’t need to declare anything because we imported the components into App.js so it is aware of them, and is now the parent of them. This is what the browser shows:
The next thing to do is pass some information from the parent to the children.
The “parent” decides to name the two Children “Bob” and “Sally” — Lets’ add this to App.js
import Brother from "./components/Brother";
import Sister from "./components/Sister";
function App() {
return (
<div>
<Brother name = "Bob" />
<Sister name = "Sally" />
</div>
);
}
export default App;
so we’ve applied “props” (properties) to the child components. We created a “prop” called name and then assigned the prop a value, which in this case is a string value of the actual names.
Now there’s two ways we can apply this to the child component. First option is to bring in ALL properties from the parent. So we could just use “props” inside the function parameter, and then to reference the specific prop we want, we specify it. For example:
function Brother(props) {
return (
<div>I am the Brother component, son of the parent! - My name is {props.name} </div>
);
}
export default Brother;
The second option is to de-structure and specify an exact prop we want to use, which in this case would be the “name” prop. If we specify exact individual props, then we enclose them in curly braces in the function:
function Sister({name}) {
return (
<div>I am the Sister component, daughter of the parent! - my name is {name} </div>
);
}
export default Sister;
both work the same way.
So we’ve stated the names in App.js and now they’ve been received by the child components, so that they can be rendered to screen:
This is parent to child communication and is pretty straightforward.
Next lets look at child to parent communication. Let’s say we wanted App.js to be aware of something when the Child asks for it.
For this example, we’ll create a form that allows the Child to make the parent aware of what toy they want for Christmas:
Here’s the form:
<form onSubmit={handleSubmit}>
<label>What I want for Christmas is:</label>
<input value={gift} onChange={handleChange}/>
<button>Submit</button>
</form>
We need a way to handle submitting this form information, so we’ll need a function called handleSubmit.
We’ll also need an input field to type in the gift <input>
Finally we’ll need a button to click on.
Because this is a form in react, we need to wire up the input field in a special way so that it tracks what’s being typed into the input field. We do this with a handleChange function:
const handleChange = (event) => {
setGift(event.target.value);
};
so whenever a change event occurs (i.e. someone presses something on the keyboard) the event.target.value will be assigned to setGift -so if I typed in “train”, then event.target.value === train and this is assigned to setGift.
setGift is a piece of state, so let’s add that:
const [gift, setGift] = useState("");
so basically, gift is being updated to event.target.value and gift itse;f is being passed back to the input field as value so that the input field actually displays what is being typed in as you type it.
Ok so now we handle the actual form submission with the handleSubmit function:
const handleSubmit = (event) => {
event.preventDefault();
onGiftRequested(gift);
};
we add in the line event.preventDefault(); so as to avoid the page instantly refreshing every time we submit the form. This is preventing the default form behavior.
We’re then calling the onGiftRequested() prop function which is a function we’ll pass into the Brother Function component via de-structuring, and then we can call it, and assign the value of gift, which is what is being submitted by the form.
Here we add in the prop into our Brother function component:
function Brother({name, onGiftRequested}) {
So here’s our complete Brother.js code for now:
import { useState } from 'react';
function Brother({name, onGiftRequested}) {
const [gift, setGift] = useState("");
const handleChange = (event) => {
setGift(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
onGiftRequested(gift);
};
return (
<div>
<div>I am the Brother component, son of the parent! - My name is {name}</div>
<form onSubmit={handleSubmit}>
<label>What I want for Christmas is:</label>
<input value={gift} onChange={handleChange}/>
<button>Submit</button>
</form>
</div>
);
}
export default Brother;
Now let’s add in the onGiftRequested prop into App.js
<Brother name = "Bob" onGiftRequested = {handleGiftRequestSon}/>
so now this Brother component has TWO props it can use for communication.
So now that we have onGiftRequested prop, we need a function to do something with it, so we’ll create a function called handleGiftRequestSon()
function App() {
const [sonGift, setSonGift] = useState("");
const handleGiftRequestSon = (gift) => {
setSonGift(gift);
};
We’ll create a piece of state to track the sons gift request, and call it sonGift.
now we’ll update this piece of state when handleGiftRequest() is called. We’re passing in a value into this function (gift) which updates the piece of state.
Remember, in the Brother.js component we called this:
onGiftRequested(gift);
So we passed “gift” to onGiftRequest.
onGiftRequest in App.js calls handleGiftRequestSon() and passes in that value of gift, and then uses it to update the sonGift piece of state.
So here is the Brother.js code:
import { useState } from 'react';
function Brother({name, onGiftRequested}) {
const [gift, setGift] = useState("");
const handleChange = (event) => {
setGift(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
onGiftRequested(gift);
};
return (
<div>
<div>I am the Brother component, son of the parent! - My name is {name}</div>
<form onSubmit={handleSubmit}>
<label>What I want for Christmas is:</label>
<input value={gift} onChange={handleChange}/>
<button>Submit</button>
</form>
</div>
);
}
export default Brother;
and here is App.js
import { useState } from "react";
import Brother from "./components/Brother";
import Sister from "./components/Sister";
function App() {
const [sonGift, setSonGift] = useState("");
const [daughterGift, setDaughterGift] = useState("");
const handleGiftRequestSon = (gift) => {
setSonGift(gift);
};
const handleGiftRequestDaughter = (gift) => {
setDaughterGift(gift);
};
return (
<div>
<Brother name = "Bob" onGiftRequested = {handleGiftRequestSon}/>
<Sister name = "Sally" onGiftRequested = {handleGiftRequestDaughter}/>
<div>The parent agrees to buy the son: {sonGift}</div>
<div>The parent agrees to buy the daughter: {daughterGift}</div>
</div>
);
}
export default App;
So you can see that we’ve passed information from the child form (Brother.js) to the Parent (App.js) by doing the following steps:
- we submit a form (remember to setup the form as per React requirement) with handleSubmit. This calls the App.js onGiftRequested() prop function
- onGiftRequested() is a prop we created for the Brother.js component, same as when we created a prop for “name”.
- onGiftRequested() calls our event handler function handleGiftRequestSon() which then sets our sonGift piece of state in App.js
- We can then return this value in the following line:
<div>The parent agrees to buy the son: {sonGift}</div>
so we’ve passed information back to App.js to be rendered onto the screen.
Finally, we actually pass that information back to the child components if we wanted to. For example:
import { useState } from 'react';
function Brother({name, onGiftRequested, agreedGiftSon, agreedGiftDaughter}) {
const [gift, setGift] = useState("");
const handleChange = (event) => {
setGift(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
onGiftRequested(gift);
};
return (
<div>
<div>I am the Brother component, son of the parent! - My name is {name}</div>
<form onSubmit={handleSubmit}>
<label>What I want for Christmas is:</label>
<input value={gift} onChange={handleChange}/>
<button>Submit</button>
</form>
<div>My dad has agreed to buy me a {agreedGiftSon} and my sister a {agreedGiftDaughter} </div>
</div>
);
}
export default Brother;
import { useState } from "react";
import Brother from "./components/Brother";
import Sister from "./components/Sister";
function App() {
const [sonGift, setSonGift] = useState("");
const [daughterGift, setDaughterGift] = useState("");
const handleGiftRequestSon = (gift) => {
setSonGift(gift);
};
const handleGiftRequestDaughter = (gift) => {
setDaughterGift(gift);
};
return (
<div>
<Brother name = "Bob" onGiftRequested = {handleGiftRequestSon} agreedGiftSon = {sonGift} agreedGiftDaughter = {daughterGift}/>
<Sister name = "Sally" onGiftRequested = {handleGiftRequestDaughter} agreedGiftSon = {sonGift} agreedGiftDaughter = {daughterGift}/>
</div>
);
}
export default App;
so we created two extra props — agreedGiftSon and agreedGiftDaughter and assigned them the state values of sonGift and daughterGift.
Because we assigned our child components those props, it means they can access them as long as we destructured them into the component function:
function Brother({name, onGiftRequested, agreedGiftSon, agreedGiftDaughter}) {
We then reference them in the return of our component:
<div>My dad has agreed to buy me a {agreedGiftSon} and my sister a {agreedGiftDaughter} </div>
the agreedGiftSon prop will then show the value assigned to it, and likewise will the agreedGiftDaughter prop.
So for example, in the browser, we might see this: