DEV Community

loading...
Cover image for State and setState Explained with Emotion

State and setState Explained with Emotion

Shaundai Person
UI Engineer at SalesLoft. Egghead Instructor building TypeScript for JavaScript Developers (tsforjs.com). Find me on twitter @shaundai
Updated on ・5 min read

This article is part of a React Family series inspired by my conversation with Jason Lengstorf on this episode of Learn with Jason.

Cover photo by Tengyart on Unsplash

In this example, we have three React components. One, <Mom /> is, of course, the parent component. The others are children of <Mom />:

  • <Toddler /> and
  • <Teen />.
1 const Mom = () => {
2 
3   return (
4     <div>
5       <Toddler />
6       <Teen />
7     </div>
8   )
9 }
Enter fullscreen mode Exit fullscreen mode

These components don't do or show anything just yet, but using this small React family, we'll see how state can be set and passed between family members components.

State at a High Level

State, like props, is an object containing information used to determine what's rendered by your React app. This could be anything, from colors to a tally to whether a modal should currently be displayed on the page or hidden from view. React state, in this example, is going to be the emotional state of our components.

Unlike props, state is managed within the component it lives on. By contrast, props is set and managed outside of the component, then passed into the component using it.

State should always live on the highest-order component that’s involved in its use. There are two reasons for this:

  • State-setting functions can only be used to set state for either (1) the component that holds the function or for (2) one of its parents. A parent, or higher-order component, cannot have a set state that lives on a child component.
  • State can be passed from parent to child (via props), information about state can be passed from child to parent (via state-setting functions), but information can only be passed from sibling-to-sibling by going through the parent (via a combination of state-setting functions and props).

We’ll explore these in further detail, but for now just understand that in our case state will need to live on <Mom /> because she is the highest-order component involved in using our state.

When a React component is rendered, it uses state's initial value. This value could be any Javascript data type or structural type including null, undefined, 0, or just a blank value (like an empty string). In our example, our parent component <Mom /> has an initial state of string ‘happy’. We set the initial state to happy using the useState hook and render it in the DOM using JSX on line 9 below.

1 const Mom = () => {
2 
3 const [emotion, setEmotion] = useState('happy')
4 
5   return (
6     <div>
7       <Toddler />
8       <Teen />
9        {emotion} //happy
10    </div>
11   )
12 }
Enter fullscreen mode Exit fullscreen mode

Setting State

Components setting their own state

Just like your emotional state, React state can be changed and maintained by a trigger (a function). In class components, these functions would be called setState functions. We’ll be using React Hooks, so our function can be called whatever we want to call it. Common convention is to call it set + WhateverTheFunctionDoes. Anytime state is changed, the component is re-rendered.

<Mom /> can hold these functions to update her own state, as below. handleMeditation() changes <Mom />’s state to ‘calm’. When a user clicks the button we've added to lines 13-15, setEmotion is triggered and our state of emotion is changed to calm.

1 const Mom = () => {
2 
3 const [emotion, setEmotion] = useState('happy')
4 
5 const handleMeditation = () => {
6   setEmotion('calm')
7 }
8 
9  return (
10    <div>
11      <Toddler />
12      <Teen />
13       <button onClick={handleMeditation}>
14         {emotion}  //changes to 'calm' once button is clicked
15       </button>
16    </div>
17   )
18 }
Enter fullscreen mode Exit fullscreen mode

Passing information from parent to child

From here, <Mom /> can pass her emotional state on to her children 😃 as props. <Toddler /> and <Teen /> will both get passed the initial props of happy. Any time <Mom />’s state changes, the emotional state of the children will be updated to match <Mom />’s.

1 const Mom = () => {
2 
3 const [emotion, setEmotion] = useState('happy')
4 
5 const handleMeditation = () => {
6   setEmotion('calm')
7 }
8 
9  return (
10    <div>
//initial props value of 'happy' changes to 'calm' in Toddler and Teen once button is clicked
11      <Toddler emotion={emotion} />
12      <Teen emotion={emotion} />
13       <button onClick={handleMeditation}>
14         {emotion}  //changes to 'calm' once button is clicked
15       </button>
16    </div>
17   )
18 }
Enter fullscreen mode Exit fullscreen mode

Passing information from child to parent

State-setting functions can also be passed down to children as props. If a child has access to any trigger - or function - that is paired with <Mom />’s state, the child thus has the ability to manipulate <Mom />’s - or the parent component’s - state.

Let’s say that <Mom /> has a different handler function called handleTemperTantrum(). <Mom /> passes this function to child <Toddler /> as a prop:

1 const Mom = () => {
2 
3 const [emotion, setEmotion] = useState('happy')
4 
5 const handleTemperTantrum = () => {
6   setEmotion('annoyed')
7 }
8 
9  return (
10    <div>
11      <Toddler handleTemperTantrum={handleTemperTantrum} />
12      <Teen />
13    </div>
14   )
15 }
Enter fullscreen mode Exit fullscreen mode

Now, <Toddler /> has the ability to manipulate update and manage <Mom />’s state on its own! <Toddler /> uses this function to change <Mom />’s state to 'annoyed' any time the button on lines 4-6 is clicked.

Toddler Component:

1 const Toddler = ({handleTemperTantrum}) => {
2 
3  return (
4    <button onClick={handleTemperTantrum}>
5      
6    </button>
7   )
8 }
Enter fullscreen mode Exit fullscreen mode

This button does not actually display anything except an empty button, though. Since the actual state of emotion still lives on <Mom />, we'll need to pass emotion down to her child. In fact, since parents can pass state down to any children as props, we can pass annoyed to both <Toddler /> and <Teen /> as props for them to use.

Mom Component:

1 const Mom = () => {
2 
3 const [emotion, setEmotion] = useState('happy')
4 
5 const handleTemperTantrum = () => {
6   setEmotion('annoyed')
7 }
8 
9  return (
10    <div>
11      <Toddler handleTemperTantrum={handleTemperTantrum} emotion={emotion} />
12      <Teen emotion={emotion} />
13    </div>
14   )
15 }
Enter fullscreen mode Exit fullscreen mode

Toddler Component:

1 const Toddler = ({handleTemperTantrum, emotion}) => {
2 
3  return (
4    <button onClick={handleTemperTantrum}>
5      {emotion} //now this will say 'calm', or will change to 'annoyed' once this button is clicked
6    </button>
7   )
8 }
Enter fullscreen mode Exit fullscreen mode

Any time <Mom />’s state is updated, <Toddler />s and <Teen />s emotional states are updated accordingly.

State-setting functions can either live on the component that the state lives on, or on one of its children.

Passing information from sibling to sibling

Siblings can pass state between one another, but they have to do it through their common parent. Just as we did in the last example, <Toddler /> can pass state to <Teen /> using this workflow:

  1. <Toddler /> has the setTemperTantrum() function, which manages and updates the state that lives on <Mom />
  2. <Toddler /> sets <Mom />’s state to annoyed
  3. <Mom /> passes annoyed as a prop to <Teen />
  4. <Teen /> accepts emotion as a prop.

Find me on Twitter or LinkedIn.

Discussion (4)

Collapse
walliskasswin profile image
Walliskasswin

Great article. There are some typos though.
A function that updates a state cannot be invoked inside of the render function or in the return block of a functional component, it will lead to infinite re-renders. You should instead pass the reference of the function.
So it is onClick={handleMeditation} instead of onClick={handleMeditation()}.

Also when passing props to functional components, to specify each props name you need to destructure.
So it's const Toddler=({handleTemperTantrum, emotion})=>{} instead of const Toddler=(handleTemperTantrum, emotion)=>{}

Collapse
shaundai profile image
Shaundai Person Author

Great catches! Normally I do a better job proofreading these before publishing :D fixed - thank you!

Collapse
carlosrafael22 profile image
Rafael Leitão

Loved this whole article with the names of the components, functions and state 😂
Great explanation with actionable examples of how to use and pass state! Awesome post, Shaundai! 😃

Collapse
shaundai profile image
Shaundai Person Author

Thanks Rafael for reading 💖