Intro
There are times in React when you'll need to use state between two sister components. You cannot directly pass state between those two sister components, so how can you use state between them? The answer is you need to host the state on the shared parent component. I'll illustrate this while using a controlled form!
Getting Started
First thing's first, you'll need to set up a React app. This is easiest by creating a new directory in your terminal, then by using these commands:
npx create-react-app my-app
cd my-app
npm start
Open the code in your code editor of choice. You can go ahead and open the App component in the "src" folder, and remove all the code inside the return()
except for the top level div, so it looks like this:
import './App.css'
function App() {
return (
<div className="App">
</div>
);
}
export default App;
Adding Components
Let's go ahead and create a couple of sister components. Create two new files called "Form.js" and "Greeting.js." If you're using Visual Studio Code, in each of them, use the keyboard shortcut "rfce" which creates some boilerplate code in the format of a React functional export component.
Those two components should now look like this:
function Form({ updateName }) {
return (
<div>test</div>
)
}
export default Form
function Greeting({ name }) {
return (
<div>test</div>
)
}
export default Greeting
(Small note that I removed the import React from 'react'
line from these components as it's not necessary on components other than App.)
Now, let's go back to our App component and import the two sister components and add them to our page. After doing so, your App component will look like this:
import './App.css'
import Form from './Form'
import Greeting from './Greeting'
function App() {
return (
<div className="App">
<Form />
<Greeting />
</div>
);
}
export default App;
Now that we have both components appearing on the DOM, let's focus on one at a time.
Form.js
Let's create a controlled form in which we ask a user to type in their name so that we can use their name in our Greeting component in order to say hi to them!
Within the div, we can create a simple form:
<form>
<label>Enter your name: </label>
<input type="text"></input>
</form>
So now, on the DOM, we can see a simple form in which a user can type. But how do we store the value of whatever the user is typing as it changes? As I mentioned previously, we want this to be a controlled form.
In order for the form to be controlled, the value of the form needs to be equal to the value we're storing in state. Basically, as the user types, state will update along with what they're typing, and the value of the input itself is the state variable, not technically what the user has typed.
Since we know we want to pass this state the Form's sister component, Greeting, we need to store state in their collective parent component, App.
App.js
Back in our App component, let's import state by adding this line at the top:
import { useState } from 'react'
Now, inside the functional component, let's create our state variable:
const [name, setName] = useState("")
Now we need to set up a function in order to set the state. We're going to pass this function, along with the variable we declared, down to our Form component as props. Every time the user types something in the form, we'll call the function (onChange). The function will simply set state to be equal to the change event's target.value
:
function updateName(input) {
setName(input.target.value)
}
Now, pass the function as a prop to the Form component along with the variable itself:
<Form name={name} updateName={updateName} />
While we're here, let's go ahead and pass down our name
variable to our Greeting component since we're know we're going to make use of it:
<Greeting name={name} />
Back in the Form component, make sure to accept the props:
function Form({ name, updateName }) {
Next, add the onChange event and set the value to be equal to our variable. These are the final steps to ensure this is a controlled form:
<input type="text" value={name} onChange={updateName}></input>
The final Form component should look like this:
function Form({ name, updateName }) {
return (
<div>
<form>
<label>Enter your name: </label>
<input type="text" value={name} onChange={updateName}></input>
</form>
</div>
)
}
export default Form
We're now done with both our Form and App components!
Greeting.js
Now, we have our Form component all set up, and with every keystroke, the name
state field is being saved in state on our App component. Let's put that on the DOM so we can greet our user! We already passed down the name
field as a prop to our Greeting component, so we need to make sure to accept it as a prop:
function Greeting({ name }) {
Next, let's just add a paragraph with a short greeting with our user's name! The component should now look like this:
function Greeting({ name }) {
return (
<div>
<p>{`Hello, ${name}!`}</p>
</div>
)
}
export default Greeting
Great, we've got the greeting on the DOM! You may notice, however, that before the user types anything, the greeting reads, "Hello, !" This is obviously less than ideal, so let's add a ternary operator to clean this up:
{name ? <p>{
Hello, ${name}!}</p> : <p>Hello!</p>}
Now, when the name
field is truthy, or, has a value, it will show the code you see between the question mark and the colon, which will include the user's name. When name
is falsey, or has no value, we'll just show the user, "Hello!"
Conclusion
Nice and tidy. Here's what the final product should look like:
So in this post, we actually learned how to send state to a sister component, how to create a controlled form, and how to use the ternary operator. I hope you can use some of these in your future projects! Thanks for reading.
-Trevor
Top comments (4)
There are many ways to do that:
State lifting(the one you described in this article)
Render props pattern
Functional children patter(this is when you inject a data as a parameter of a children props e.g children({state})
Higher order component/context (note that behind the hood context uses higher order component)
But overall, the pattern you describe is enough in most cases. We call it state lifting pattern
Great comment, thanks for pointing out the other options!
can use render props its so cool
I'll take a look, thank you!