320H.2 - React State and Props
Learning Objectives
By the end of this lesson, learners will be able to:
- Describe React state and props.
- Use React state and props to pass information between components.
- Build a simple application that uses state and props.
CodeSandbox
This lesson uses CodeSandbox as one of its tools.
If you are unfamiliar with CodeSandbox, or need a refresher, please visit our reference page on CodeSandbox for instructions on:
- Creating an Account
- Making a Sandbox
- Navigating your Sandbox
- Submitting a link to your Sandbox to Canvas
React State and Props
Like most applications, a React component may have state data or information. State is simply the particular condition that something is in at a given point in time. In the context of a computer application, state refers to the values of state variables and state objects that describe its stored inputs and data.
- State can only be changed by the component that "owns" that state.
- In React, when a component's state is changed, that entire component is re-rendered, including all of its child components.
-
A "stateful" component passes any state needed by child components as props (short for properties).
- Props are arbitrary inputs passed as arguments into React components.
- Props are accessible as key/value pairs on a
props
object. Thisprops
object is passed to the component every time it is rendered. - Props in a component are always read-only.
Props is short for properties. We know that a component can have state (a view based on data). As we would continue to build out functionality, our one component would get very complex, and the code could expand to hundreds or thousands of lines. Maintaining such a large component would be really difficult.
Props allow a component to receive data from its parent component.
-
Props can only be sent from a parent to a child.
- If the parent needs data from the child, it should send a function as a prop. Then, the child can pass its data to the function as an argument.
- Anything can be sent as a prop, including JSX.
As an example, here is a simple React component that accepts a prop and incorporates its name
value:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
Notice that props are passed into components just like attributes would be passed into an HTML element. This can lead to some confusion if you've never dealt with props before. Take the following example:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" style={{ color: "red" }} />;
If you test this code, you'll notice that setting style
does not change the font color. That's because style
on the Welcome
component is not an HTML attribute, it's a React prop. In order to incorporate this value as intended, we would need to do the following:
function Welcome(props) {
return <h1 style={props.style}>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" style={{ color: "red" }} />;
Now, we have red text!
But why does the style
prop work the way we want it to on the h1
component? ReactDOM conforms to the HTML standard, allowing us to use standard HTML attributes on React's built-in components!
Here is a more complex example of setting parent variables with child data and also using props within the receiving component:
// The Child Component
const Child = props => {
// change the value of someVariable using function sent via props
props.setter(8)
return <h1>{props.stuff}</h1>
}
// The Parent Component
const Parent = props => {
let someVariable
// function to set someVariable
const setSV = data => {
someVariable = data
}
// send down two props, stuff and setter
return <Child stuff="hello world" setter={setSV} />
}
State Management
State is the most important concept in React (and most application development). Your app is "reactive" because you have state for data your UI depends on. As apps get more complex, deciding how to handle state and where it should be housed can get quite daunting.
As a general rule of thumb, ask yourself: How many components use this piece of state?
- 0-1: The state should be in the one component using it and nowhere else.
- 2-5: The state should be located in a parent all of the components share, but as low in the component tree as possible.
- 5+: Time to consider using Context or Redux (more on these later).
Lifting State
The concept of lifting state occurs when siblings need to share state with each other. The lifting state pattern looks like this:
- The state is housed in the parent of the two siblings.
- The parent passes a function as props to the sender to alter the parent's state.
- The parent passes the state itself as a prop to the receiver to receive the updated state.
As an example of lifting state:
// Component receives function as prop to update parent's state
const SenderChild = props => {
return <button onClick={() => props.update("Goodbye")}>Click Me</button>
}
// Component receives parent's state
const ReceiverChild = props => {
return <h1>{props.value}</h1>
}
// The parent who passes props to both children
const Parent = props => {
// The State
// We'll talk about this "useState" thing (called a "hook") in the next lesson!
const [state, setState] = useState("Hello")
// Function to update state to send to child
const updateState = data => setState(data)
// We pass the function and the state as props to the children
return (
<div>
<ReceiverChild value={state} />
<SenderChild update={updateState} />
</div>
)
}
Prop Drilling
This is the inevitable tragedy that occurs when your components trees grow to several layers. Imagine a piece of state is in a component that is needed in a grandchild component... you'd have to do the following.
const Parent = props => <Child cheese="gouda" />
const Child = props => <GrandChild data={cheese} />
const GrandChild = props => <h1>{props.data}</h1>
This is prop drilling: the Parent
passes cheese
to child, who passes the same data as data
to GrandChild
. Imagine if it was a Great-Great-Grandchild... that's a lot of typing just so one component can receive a single piece of data.
There are several solutions to this:
- React Context
- React
useReducer
Hook - The TaskRunner Pattern
- Redux
- And many more... (MobX, State Machines, ...)
We will cover many of these solutions in future lessons.
Handling Props
There are a few easy ways to handle props that will make your code simpler and more readable.
Destructuring Props
If you know the names of the props your component will receive, you can destructure them and save the hassle of typing props
.
const Component = ({ name, age }) => (
<div>
<h1>{name}</h1>
<h2>{age}</h2>
</div>
)
When destructuring props, you can also set default values:
const Component = ({ name, age = 0 }) => (
<div>
<h1>{name}</h1>
<h2>{age}</h2>
</div>
)
Now, if Component
is rendered without an age
prop, the age
will be set to 0.
Spreading Props
If you are giving a component a LOT of props and it may be a little messy to type them inline, then bundle them in an object and spread (...
) them.
So instead of...
<Component name="John Doe" age={35} website="deeperThanCode.com" />
Do this...
const props = {
name: "John Doe",
age: 35,
website: "deeperThanCode.com"
}
return <Component {...props}>
When you consider that a component could have many, many props, using the spread operator can make things much more legible. This can also be used when a parent component needs to forward all of its props to a child component.
For example, these two code blocks are equivalent:
function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
If you are using spread syntax too often, however, it may indicate that something is incorrect or inefficient with the way you're structuring your application.
props.children
All components have a prop by default called "children
", the children
prop represents any elements wrapped in the components opening and closing tag. So imagine below we have a <Container>
component we use to block off content.
const Container = (props) => {
const style = {
width: "90%",
margin: "auto,
border: "1px solid green"
}
return <div style={style}> {props.children} </div>
}
const App = (props) => {
return <Container><h1>Hello World</h1></Container>
}
In this case, anything between the opening and closing <Container>
tag is stored in props.children
and will render inside of a <div>
with a green border.
The children
prop is often used in this way for visual wrappers and container-like elements, since they format their contents without needing to "know" what's being rendered inside of them. Panels, grids, cards, and other elements can take advantage of this prop.
Dynamic and Changing Props
The value of a prop does not need to be static over the course of time. In order to make interactive, dynamic content, props and state need to change, and the components that use them need to change in response. Luckily, React handles this by re-rendering any component and all of its children any time any state within those components change.
Take this example from the React documentation:
Try changing the color using the dropdown menu, and notice that the time is constantly updating.
The inner working of this are currently hidden within App.js
because this uses a few concepts we are not yet familiar with, like React hooks. We'll begin talking about what hooks are and discussing one of the most important React hooks (useState
) in the next lesson.
It is important to note that, even though props can reflect a component's data at any point in time, they are immutable. When a component needs to change its props in response to user interaction or new data, it has to "ask" its parent component to pass different props. These new props are a completely new object, without any reference to the one prior.
We will discover why this behavior is preferable as we continue learning React!
Rendering
Consider that most state is held at or near the top of the component hierarchy. Further, you now know that a component, and all of its children are re-rendered if any state changes.
Rendering happens frequently in a React app (whenever state changes), but thanks to React's ingenious design, it's very fast and efficient because:
- First, React renders to an in-memory representation of the DOM, known as the Virtual DOM.
- After the rendering is done, React compares the latest Virtual DOM to the previous Virtual DOM and computes only the "difference", known as the diff.
- React then updates the browser's actual DOM with the actual changes (the computed diff).

Imagine we have a component that represents a shopping cart. At first, our cart is empty. Our state would likely be an empty array. Then, we add an item into our cart. We'd push an object like this one into our cart:
{
itemName: 'Jar of Speculoos',
description: 'Imagine butter cookies dissolved in butter, made into cookie butter and stored in a jar. Stop imagining and now buy this!',
price: 6.99
}
Now our view of the shopping cart will change, based on the state of the shopping cart.
Practice Activity: Working with Props
Take a few minutes to go over some simple React challenges adapted from the React documentation below. Work through each of these problems, and then we'll discuss the solutions together as a class. Note that if your solution differs from others, that may be okay! Not every valid solution is the same.
In the following sandbox, the Gallery
component within App.js
contains two very similar markups for two different profiles. In component-driven development, when two things are this similar, they should probably be a single component to prevent rewriting code and enhance scalability. Create a new component called Profile
, and extract some of the code out of Gallery
to achieve the same result that is shown below. You will need to decide what kind of props need to be passed into each Profile
component in order to maintain the current contents.
In this next sandbox, Avatar
receives a numeric size
prop which determines the <img>
width and height. The size
prop is set to 40
in this example. However, if you open the image in a new tab, you’ll notice that the image itself is larger (160 pixels). The real image size is determined by which thumbnail size you’re requesting.
Change the Avatar
component to request the closest image size based on the size
prop. Specifically, if the size
is less than 90
, pass 's'
(“small”) rather than 'b'
(“big”) to the getImageUrl
function. Verify that your changes work by rendering avatars with different values of the size
prop and opening images in a new tab.
In this final sandbox, we wrap multiple elements within the "card" and "card-content" divs. Extract these wrapper divs to a new component called Card
, and use the children
prop to pass different JSX to the Card
component in order to recreate the original result below.
Conditional Rendering
We have already touched on conditional rendering in the previous lesson, but now that you are familiar with props, take a look at the examples below from a new perspective. Try to think of ways that conditional rendering via the use of props could be useful in an interactive application.
Also take note that, because JSX expression are expressions, you can conditionally render entire components. Examples of this will follow.
If ... Else with Multiple Returns
const IsEven = props => {
if (props.number % 2 === 0) {
return <h1>It is even</h1>
} else {
return <h1>It is odd</h1>
}
}
If ... Else with Single Return
const IsEven = props => {
let result
if (props.number % 2 === 0) {
result = <h1>It is even</h1>
} else {
result = <h1>It is odd</h1>
}
return result
}
Returning a Ternary Operator
const IsEven = props => {
return props.number % 2 === 0 ? <h1>It is even</h1> : <h1>It is odd</h1>
}
Returning a Ternary Operator with Variables
const IsEven = props => {
const condition = props.number % 2 === 0
const ifTrue = () => <h1>It is even</h1>
const ifFalse = () => <h1>It is odd</h1>
return condition ? ifTrue() : ifFalse()
}
Creating Conditional Classes
const Modal = props => (
<div className={props.visible ? "active" : ""}>{props.children}</div>
)
const Modal = props => {
const divClass = props.visible ? "active" : "";
return <div className={divClass}>{props.children}</div>
}
Creating Conditional Style
const Modal = props => (
<div style={{ display: props.visible ? "block" : "none" }}>
{props.children}
</div>
)
const Modal = props => {
const divDisplay = props.visible ? "block" : "none";
return <div style={{ display: divDisplay }}>{props.children}</div>
}
const Modal = props => {
const divStyle = {
display: props.visible ? "block" : "none",
}
return <div style={divStyle}>{props.children}</div>
}
Using Object Keys
const Component = props => {
const result = {
good: <h1>Good</h1>,
bad: <h1>Bad</h1>,
ugly: <h1>Ugly</h1>,
}
return result[props.key]
}
Switch Statements
const Hello = (props) => {
switch(props.language){
case "eng":
return <h1>Hello</h1>
case "esp":
return <h1>Hola</h1>
default:
return: <h1>No Language Detected</h1>
}
}
As a practical example of how we might use conditional rendering of entire components, let's assume we have a navigation bar that includes a user menu and login/logout options. Here's how we might put a component like that together (omitting other necessary components and their logic).
function Navbar({ isLoggedIn = false }) {
return (
<NavContainer>
<Menu />
<SearchBar />
{ isLoggedIn && <UserMenu /> }
{ isLoggedIn ? <LogoutBtn /> : <LoginBtn /> }
</NavContainer>
);
}
The above example could be made more concise by including the UserMenu
component within the ternary operator, but it illustrates a useful shortcut: you can use &&
to quickly render certain items based on a boolean value.
As an additional example, you could render a string like this: { isHello && 'World' }
, which would only show 'World'
when isHello
is true
. When React encounters false
in its JSX tree, it considers that a null value and renders nothing in that place.
Using Arrays in React
Oftentimes we may want generate JSX for many elements of an array. The standard way of doing so is using the Array.map()
method.
For example, if we wanted to create an element for each dog object in the following array:
const Component = () => {
// An array of dogs.
const dogs = [
{ name: "Sparky", age: 5 },
{ name: "Spot", age: 5 },
{ name: "Ralph", age: 5 },
{ name: "Fido", age: 5 },
]
// Map over the dogs array and create an array of JSX for each dog.
const dogJSX = dogs.map(dog => {
// We return JSX for each dog in the array, which we store in the dog variable. Essentially, we are looping over dog of dogs.
return (
<div>
<h1>{dog.name}</h1>
<h2>{dog.age}</h2>
</div>
)
})
// The component returns JSX that uses the dogJSX array.
return <div>{dogJSX}</div>
}
Now, when we use this component, it will create four separate <div>
s. We could have hard-coded this into our component, but Array.map()
provides cleaner, more concise code, and - more importantly - scalability. What happens when we need to create a component made from an array of 100 items, and that array is a part of a changing state? Array.map()
is the way to go.
Iterating Over an Object in React
Using Objects.keys()
to generate an array of strings that are the keys of the objects properties also allows you to iterate over the array and generate JSX for each property.
const Component = () => {
const Person = {
name: "Jane Doe",
age: "35",
email: "ceo@myfuturebusiness.com",
}
return Object.keys(Person).map((key, index) => {
return (
<h2>
{key}: {Person[key]}
</h2>
)
})
}
Props and Arrays
We just mentioned you can use Array methods like map
and filter
to build React components, but you can also use them to provide values to components via props, and build composite components using this method.
Take a look through the example below. App.js
contains a List
component that builds the list of scientists from a people
array being stored in data.js
.
What if we wanted to use this structure elsewhere? Right now, each "person" is a part of the List
component, when we could very well extract them to a Person
component. Take a look at the differences between the App.js
files above and below. Below, we map()
to pass props to a new Person
component, rather than building all of the people directly into List
.
While this does not change the behavior of the example at all, it gives us access to a new reusable Person
component and also makes our List
component more easy to understand at a glance. If we had additional people to add to this list beyond those found in the people
array, we could easily do so without repeating code.
As a practice activity, add another person to the List
component by manually appending a new element to listItems
. Check data.js
for the correct format and structure of a person
object.
Note: When examining the
data.js
file, you will notice the comment "user in JSX as a kay" next to eachid
field. JSX elements directly inside of amap()
method call always need keys!
Keys tell React which array item each component corresponds to, so that it can match them up later. This becomes important if your array items can move (e.g. due to sorting), get inserted, or get deleted. A well-chosen key helps React infer what exactly has happened, and make the correct updates to the DOM tree.
Rather than generating keys on the fly, you should include them in your data as done in the example above. Keys must be unique among siblings. Having the same key for JSX nodes in different arrays, however, is fine. Keys also must not change.
Here's what the React documentation has to say about why React needs keys:
"Imagine that files on your desktop didn’t have names. Instead, you’d refer to them by their order — the first file, the second file, and so on. You could get used to it, but once you delete a file, it would get confusing. The second file would become the first file, the third file would be the second file, and so on.
File names in a folder and JSX keys in an array serve a similar purpose. They let us uniquely identify an item between its siblings. A well-chosen key provides more information than the position within the array. Even if the position changes due to reordering, the key lets React identify the item throughout its lifetime.
You might be tempted to use an item’s index in the array as its key. In fact, that’s what React will use if you don’t specify a key
at all. But the order in which you render items will change over time if an item is inserted, deleted, or if the array gets reordered. Index as a key often leads to subtle and confusing bugs.
Similarly, do not generate keys on the fly, e.g. with key={Math.random()}
. This will cause keys to never match up between renders, leading to all your components and DOM being recreated every time. Not only is this slow, but it will also lose any user input inside the list items. Instead, use a stable ID based on the data.
Note that your components won’t receive key
as a prop. It’s only used as a hint by React itself. If your component needs an ID, you have to pass it as a separate prop: <Profile key={id} userId={id} />
."
Practice Activity: Arrays within Arrays with Props
The CodeSandbox below contains another bit of code from the React documentation. Data for this activity is in the data.js
file. Try to accomplish the following:
- Create a
Recipe
component that displays an<h2>
containing the name of the recipe, and an unordered list of ingredients usingmap
. - Use
map
to map over all of the recipes inrecipes
, and display each of them. Pass props as necessary.
If we did not create a new Recipe component, what would our code look like? We would have two nested map()
calls: one to handle the recipes
and another to handle each recipe's ingredients
. What is the issue with having nested map()
calls?
Moving Forward
So far, all of our usage of props has been static, but we began this lesson with the topic of state. While state can be static, it rarely ever is. In order to work with dynamic, changing state, we need to talk about React hooks, specifically useState
.
In the next lesson, we'll cover how to implement the useState
hook to enable interactivity and dynamic content.