Each of its event handlers calls `setTasks` in order to update the state. As this component grows, so does the amount of state logic sprinkled throughout it. To reduce this complexity and keep all your logic in one easy-to-access place, you can move that state logic into a single function outside your component. **This function is called a "reducer."** Reducers are a different way to handle state, and you can migrate from `useState` to `useReducer` in three steps.
Each of its event handlers calls `setTasks` in order to update the state. As this component grows, so does the amount of state logic sprinkled throughout it. To reduce this complexity and keep all your logic in one easy-to-access place, you can move that state logic into a single function outside your component, **called a "reducer."**
Reducers are a different way to handle state. You can migrate from `useState` to `useReducer` in three steps:
1. **Move** from setting state to dispatching actions.
2. **Write** a reducer function.
3. **Use** the reducer from your component.
### Step 1: Move from setting state to dispatching actions
@ -374,7 +379,7 @@ const sum = arr.reduce(
); // 1 + 2 + 3 + 4 + 5
```
The function you pass to `reduce` is known as a "reducer". It takes the _result so far_ and the _current item,_ then it returns the _next result._ React reducers are an example of the same idea: they take the _state so far_ and the _action_ then return the _next state._ In this way, they accumulate actions over time into state.
The function you pass to `reduce` is known as a "reducer". It takes the _result so far_ and the _current item,_ then it returns the _next result._ React reducers are an example of the same idea: they take the _state so far_ and the _action_, and return the _next state._ In this way, they accumulate actions over time into state.
You could even use the `reduce()` method with an `initialState` and an array of `actions` to calculate the final state by passing your reducer function to it:
@ -385,10 +390,10 @@ import tasksReducer from './tasksReducer.js';
There are a couple of ways you could do in the "Send" button event handler. One approach is to show an alert and then dispatch an `edited_message` action with an empty `message`:
There are a couple of ways you could do it in the "Send" button event handler. One approach is to show an alert and then dispatch an `edited_message` action with an empty `message`:
<Sandpack>
@ -2373,7 +2381,7 @@ textarea {
</Sandpack>
Notably, you didn't need to change any of the event handlers to implement this different behavior. If you did not use a reducer, you would have to change every event handler that updates the state.
Notably, you didn't need to change any of the event handlers to implement this different behavior. Without a reducer, you would have to change every event handler that updates the state.
@ -532,7 +532,7 @@ Read **[Adding Interactivity](/learn/adding-interactivity)** to learn how to upd
You'll often face a choice of _what exactly_ to put into state. Should you use one state variable or many? An object or an array? How should you [structure your state](/learn/choosing-the-state-structure)? The most important principle is to **avoid redundant state**. If some information never changes, it shouldn't be in state. If some information is received from parent by props, it shouldn't be in state. And if you can compute something from other props or state, it shouldn't be in state either!
For example, this form has a redundant `fullName` state variable:
For example, this form has a **redundant**`fullName` state variable:
<Sandpack>
@ -556,6 +556,7 @@ export default function Form() {
return (
<>
<h2>Let’s check you in</h2>
<label>
First name:{' '}
<input
@ -570,9 +571,9 @@ export default function Form() {
onChange={handleLastNameChange}
/>
</label>
<h3>
Your full name is: {fullName}
</h3>
<p>
Your ticket will be issued to: <b>{fullName}</b>
</p>
</>
);
}
@ -607,6 +608,7 @@ export default function Form() {
return (
<>
<h2>Let’s check you in</h2>
<label>
First name:{' '}
<input
@ -621,9 +623,9 @@ export default function Form() {
This might seem like a small change, but many bugs in React apps are fixed this way!
Sometimes, you want the state of two components to always change together. To do it, remove state from both of them, move it to their closest common parent, and then pass it down to them via props. This is known as ["lifting state up"](/learn/sharing-state-between-components), and it's one of the most common things you will do writing React code. For example, in an accordion like below, only one panel should be active at a time. Instead of keeping the active state inside each individual panel, the parent component holds the state and specifies the props for its children.
<Sandpack>
@ -646,20 +650,20 @@ export default function Accordion() {
With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
</Panel>
<Panel
title="Recipe"
title="Etymology"
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
Heat the milk and put tea bags into the pan.
Add the cinnamon stick.
The name comes from <spanlang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <ilang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
@ -24,27 +24,27 @@ As your application grows, it helps to be more intentional about how your state
With React, you won't modify the UI from code directly. For example, you won't write commands like "disable the button", "enable the button", "show the success message", etc. Instead, you will describe the UI you want to see for the different visual states of your component ("initial state", "typing state", "success state"), and then trigger the state changes in response to user input. This is similar to how designers think about UI.
Here is a feedback form built using React. Note how it uses the `status` state variable to determine whether to enable or disable the submit button, and whether to show the success message instead.
Here is a quiz form built using React. Note how it uses the `status` state variable to determine whether to enable or disable the submit button, and whether to show the success message instead.
<Sandpack>
```js
import { useState } from 'react';
export default function FeedbackForm() {
const [message, setMessage] = useState('');
export default function Form() {
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
const [status, setStatus] = useState('typing');
if (status === 'success') {
return <h1>Thank you!</h1>
return <h1>That's right!</h1>
}
async function handleSubmit(e) {
e.preventDefault();
setStatus('submitting');
try {
await submitForm();
await submitForm(answer);
setStatus('success');
} catch (err) {
setStatus('typing');
@ -53,39 +53,45 @@ export default function FeedbackForm() {
}
function handleTextareaChange(e) {
setMessage(e.target.value);
setAnswer(e.target.value);
}
return (
<formonSubmit={handleSubmit}>
<textarea
value={message}
onChange={handleTextareaChange}
disabled={status === 'submitting'}
/>
<br/>
<buttondisabled={
message.length === 0 ||
status === 'submitting'
}>
Submit
</button>
{error !== null &&
<pclassName="Error">
{error.message}
</p>
}
</form>
<>
<h2>City quiz</h2>
<p>
In which city is there a billboard that turns air into drinkable water?
</p>
<formonSubmit={handleSubmit}>
<textarea
value={answer}
onChange={handleTextareaChange}
disabled={status === 'submitting'}
/>
<br/>
<buttondisabled={
answer.length === 0 ||
status === 'submitting'
}>
Submit
</button>
{error !== null &&
<pclassName="Error">
{error.message}
</p>
}
</form>
</>
);
}
function submitForm() {
function submitForm(answer) {
// Pretend it's hitting the network.
return new Promise((resolve, reject) => {
setTimeout(() => {
let shouldError = Math.random() > 0.5;
let shouldError = answer.toLowerCase() !== 'lima'
if (shouldError) {
reject(new Error('Something went wrong'));
reject(new Error('Good guess but a wrong answer. Try again!'));
} else {
resolve();
}
@ -110,7 +116,7 @@ Read **[Reacting to Input with State](/learn/reacting-to-input-with-state)** to
Structuring state well can make a difference between a component that is pleasant to modify and debug, and one that is a constant source of bugs. The most important principle is that state shouldn't contain redundant or duplicated information. If there's some unnecessary state, it's easy to forget to update it, and introduce bugs!
For example, this form has a redundant `fullName` state variable:
For example, this form has a **redundant**`fullName` state variable:
<Sandpack>
@ -134,6 +140,7 @@ export default function Form() {
return (
<>
<h2>Let’s check you in</h2>
<label>
First name:{' '}
<input
@ -148,9 +155,9 @@ export default function Form() {
onChange={handleLastNameChange}
/>
</label>
<h3>
Your full name is: {fullName}
</h3>
<p>
Your ticket will be issued to: <b>{fullName}</b>
</p>
</>
);
}
@ -185,6 +192,7 @@ export default function Form() {
return (
<>
<h2>Let’s check you in</h2>
<label>
First name:{' '}
<input
@ -199,9 +207,9 @@ export default function Form() {
With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
</Panel>
<Panel
title="Recipe"
title="Etymology"
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
Heat the milk and put tea bags into the pan.
Add the cinnamon stick.
The name comes from <spanlang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <ilang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
</Panel>
</>
);
@ -294,7 +304,7 @@ Read **[Sharing State Between Components](/learn/sharing-state-between-component
When you re-render a component, React needs to decide which parts of the tree to keep (and update), and which parts to discard or re-create from scratch. In most cases, React's automatic behavior works well enough. By default, React preserves the parts of the tree that "match up" with the previously rendered component tree.
However, sometimes this is not what you want. For example, in this app, typing a message and then switching the recipient does not reset the input. This can cause the user to accidentally send a message to the wrong person:
However, sometimes this is not what you want. For example, in this app, typing a message and then switching the recipient does not reset the input. This can make the user accidentally send a message to the wrong person:
<Sandpack>
@ -389,7 +399,7 @@ textarea {
</Sandpack>
React lets you override the default behavior, and *force* a component to reset its state by passing it a different `key`, like `<Chat key={email} />`. This tells React that if the recipient is different, it should be considered a *different*`Chat` component, and it needs to be re-created from scratch with the new data (and UI like inputs). Now switching between the recipients always resets the input field--even though you render the same component.
React lets you override the default behavior, and *force* a component to reset its state by passing it a different `key`, like `<Chat key={email} />`. This tells React that if the recipient is different, it should be considered a *different*`Chat` component that needs to be re-created from scratch with the new data (and UI like inputs). Now switching between the recipients always resets the input field--even though you render the same component.
<Sandpack>
@ -492,7 +502,7 @@ Read **[Preserving and Resetting State](/learn/preserving-and-resetting-state)**
## Extracting state logic into a reducer
Components with many state updates spread across many event handlers can get overwhelming. For these cases, you can consolidate all the state update logic outside your component in a single function, called a "reducer." Your event handlers become concise because they only specify the user "actions." At the bottom of the file, the reducer function specifies how the state should update in response to each action!
Components with many state updates spread across many event handlers can get overwhelming. For these cases, you can consolidate all the state update logic outside your component in a single function, called "reducer." Your event handlers become concise because they only specify the user "actions." At the bottom of the file, the reducer function specifies how the state should update in response to each action!
<Sandpack>
@ -531,6 +541,7 @@ export default function TaskBoard() {
return (
<>
<h1>Prague itinerary</h1>
<AddTask
onAddTask={handleAddTask}
/>
@ -572,9 +583,9 @@ function tasksReducer(tasks, action) {
@ -688,7 +699,7 @@ Read **[Extracting State Logic into a Reducer](/learn/extracting-state-logic-int
## Passing data deeply with context
Usually, you will pass information from a parent component to a child component via props. But passing props can become cumbersome if you need to pass some prop deeply through the tree, or if many components in the UI tree need the same prop. Context lets the parent component make some information available to any component in the tree below it—no matter how deep it is—without passing it explicitly through props.
Usually, you will pass information from a parent component to a child component via props. But passing props can become inconvenient if you need to pass some prop through many components, or if many components need the same information. Context lets the parent component make some information available to any component in the tree below it—no matter how deep it is—without passing it explicitly through props.
Here, the `Heading` component determines its heading level by "asking" the closest `Section` for its level. Each `Section` tracks its own level by asking the parent `Section` and adding one to it. Every `Section` provides information to all components below it without passing props--it does that through context.
@ -799,11 +810,12 @@ With this approach, a parent component with complex state manages it with a redu
```js App.js
import AddTask from './AddTask.js';
import TaskList from './TaskList.js';
import { TasksProvider } from './TaskBoardContext.js';
import { TasksProvider } from './TasksContext.js';
export default function TaskBoard() {
return (
<TasksProvider>
<h1>Day off in Kyoto</h1>
<AddTask/>
<TaskList/>
</TasksProvider>
@ -811,15 +823,10 @@ export default function TaskBoard() {
}
```
```js TaskBoardContext.js
import {
createContext,
useContext,
useReducer
} from 'react';
const TaskBoardContext = createContext(null);
```js TasksContext.js
import { createContext, useContext, useReducer } from 'react';
const TasksContext = createContext(null);
const TasksDispatchContext = createContext(null);
export function TasksProvider({ children }) {
@ -829,18 +836,18 @@ export function TasksProvider({ children }) {
);
return (
<TaskBoardContext.Providervalue={tasks}>
<TasksContext.Providervalue={tasks}>
<TasksDispatchContext.Provider
value={dispatch}
>
{children}
</TasksDispatchContext.Provider>
</TaskBoardContext.Provider>
</TasksContext.Provider>
);
}
export function useTasks() {
return useContext(TaskBoardContext);
return useContext(TasksContext);
}
export function useTasksDispatch() {
@ -875,15 +882,15 @@ function tasksReducer(tasks, action) {
@ -4,7 +4,7 @@ title: Passing Data Deeply with Context
<Intro>
Usually, you will pass information from a parent component to a child component via props. But passing props can become cumbersome if you need to pass a prop deeply through the tree, or if many components in the UI tree need the same prop. Context lets the parent component make some information available to _any_ component in the tree below it--no matter how deep--without passing it explicitly through props.
Usually, you will pass information from a parent component to a child component via props. But passing props can become verbose and inconvenient if you have to pass them through many components in the middle, or if many components in your app need the same information. Context lets the parent component make some information available to any component in the tree below it—no matter how deep—without passing it explicitly through props.
</Intro>
@ -19,7 +19,7 @@ Usually, you will pass information from a parent component to a child component
## The problem with passing props
[Passing props](/learn/passing-props-to-a-component) is a great way to explicitly pipe data through your UI tree to the components that use it. But it can become cumbersome when you need to pass some prop deeply through the tree, or if many components need the same prop. The nearest common ancestor could be far removed from the components that need data, and lifting state up that high can lead to a situation sometimes called "prop drilling."
[Passing props](/learn/passing-props-to-a-component) is a great way to explicitly pipe data through your UI tree to the components that use it. But it can become verbose and inconvenient when you need to pass some prop deeply through the tree, or if many components need the same prop. The nearest common ancestor could be far removed from the components that need data, and [lifting state up](/learn/sharing-state-between-components) that high can lead to a situation sometimes called "prop drilling."
<imgalt="Lifting state up vs prop drilling"src="/images/docs/sketches/s_prop-drilling.png"/>
Now both `Heading` and `Section` read the `LevelContext` to figure out how "deep" they are. And the `Section` wraps its children into the `LevelContext` to specify that anything inside of it is at a "deeper" level.
>This example uses heading levels because they demonstrate visually how nested components can override context. But context is useful for many other use cases too. You can use it to pass down any information needed by the entire subtree: the current color theme, the currently logged in user, and so on.
>This example uses heading levels because they show visually how nested components can override context. But context is useful for many other use cases too. You can use it to pass down any information needed by the entire subtree: the current color theme, the currently logged in user, and so on.
## Context passes through intermediate components
@ -687,8 +687,8 @@ export default function ProfilePage() {
<Section>
<Heading>My Profile</Heading>
<Post
title="Hey stranger"
body="Welcome to my page!"
title="Hello traveller!"
body="Read about my adventures."
/>
<AllPosts/>
</Section>
@ -709,12 +709,12 @@ function RecentPosts() {
<Section>
<Heading>Recent Posts</Heading>
<Post
title="Recent trip"
body="This was a nice journey."
title="Flavors of Lisbon"
body="...those pastéis de nata!"
/>
<Post
title="My first post"
body="Hello there!"
title="Buenos Aires in the rhythm of tango"
body="I loved it!"
/>
</Section>
);
@ -805,15 +805,15 @@ You didn't do anything special for this to work. A `Section` specifies the conte
How context works might remind you of [CSS property inheritance](https://developer.mozilla.org/en-US/docs/Web/CSS/inheritance). In CSS, you can specify `color: blue` for a `<div>`, and any DOM node inside of it, no matter how deep, will inherit that color unless some other DOM node in the middle overrides it with `color: green`. Similarly, in React, the only way to override some context coming from above is to wrap children into a context provider with a different value.
In CSS, different properties like `color` and `background-color` don't override each other. You can set all `<div>`'s `color` to red without impacting `background-color`. Similarly, **different React contexts don't override each other**. Each context that you make with `createContext()` is completely separate from each other, and ties together components using and providing *that particular* context. One component may use or provide many different contexts without a problem.
In CSS, different properties like `color` and `background-color` don't override each other. You can set all `<div>`'s `color` to red without impacting `background-color`. Similarly, **different React contexts don't override each other**. Each context that you make with `createContext()` is completely separate from other ones, and ties together components using and providing *that particular* context. One component may use or provide many different contexts without a problem.
## Before you use context
Context is very tempting to use! However, this also means it's too easy to overuse it. **Just because you need to pass some props several levels deep, doesn't mean you need to put that information into context.**
Context is very tempting to use! However, this also means it's too easy to overuse it. **Just because you need to pass some props several levels deep doesn't mean you should put that information into context.**
Here's a few alternatives you should consider before using context:
1. **Start by [passing props](/learn/passing-props-to-a-component).** If your components are not trivial, it's not unusual to pass a dozen props down through a dozen of components. It may feel like a slog, but it makes it very clear which components use which data! The person maintaining your code will be glad you've made the data flow explicit with props.
1. **Start by [passing props](/learn/passing-props-to-a-component).** If your components are not trivial, it's not unusual to pass a dozen props down through a dozen components. It may feel like a slog, but it makes it very clear which components use which data! The person maintaining your code will be glad you've made the data flow explicit with props.
2. **Extract components and [pass JSX as `children`](/learn/passing-props-to-a-component#passing-jsx-as-children) to them.** If you pass some data through many layers of intermediate components that don't use that data (and only pass it further down), this often means that you forgot to extract some components along the way. For example, maybe you pass data props like `posts` to visual components that don't use them directly, like `<Layout posts={posts} />`. Instead, make `Layout` take `children` as a prop, and render `<Layout><Posts posts={posts} /></Layout>`. This reduces the number of layers between the component specifying the data and the one that needs it.
If neither of these approaches works well for you, consider context.
@ -844,15 +844,13 @@ In general, if some information is needed by distant components in different par
</Recap>
<Challenges>
### Replace prop drilling with context
In this example, toggling the checkbox changes the `thumbnailSize` prop passed to each `<Avatar>`. The checkbox state is held in the top-level `App` component, but each `<Avatar>` needs to be aware of it.
In this example, toggling the checkbox changes the `imageSize` prop passed to each `<PlaceImage>`. The checkbox state is held in the top-level `App` component, but each `<PlaceImage>` needs to be aware of it.
Currently, `App` passes `thumbnailSize` to `List`, which passes it to each `Profile`, which passes it to the `Avatar`. Remove the `thumbnailSize` prop, and instead pass it from the `App` component directly to `Avatar`.
Currently, `App` passes `imageSize` to `List`, which passes it to each `Place`, which passes it to the `PlaceImage`. Remove the `imageSize` prop, and instead pass it from the `App` component directly to `PlaceImage`.
You can declare context in `Context.js`.
@ -860,69 +858,64 @@ You can declare context in `Context.js`.
```js App.js
import { useState } from 'react';
import { people } from './data.js';
import { places } from './data.js';
import { getImageUrl } from './utils.js';
export default function App() {
const [
largeThumbnails,
setLargeThumbnails
] = useState(false);
const thumbnailSize = largeThumbnails ? 100 : 50;
const [isLarge, setIsLarge] = useState(false);
const imageSize = isLarge ? 150 : 100;
return (
<>
<label>
<input
type="checkbox"
checked={largeThumbnails}
checked={isLarge}
onChange={e => {
setLargeThumbnails(e.target.checked);
setIsLarge(e.target.checked);
}}
/>
Use large thumbnails
Use large images
</label>
<hr/>
<ListthumbnailSize={thumbnailSize}/>
<ListimageSize={imageSize}/>
</>
)
}
function List({ thumbnailSize }) {
const listItems = people.map(person =>
<likey={person.id}>
<Profile
person={person}
thumbnailSize={thumbnailSize}
function List({ imageSize }) {
const listItems = places.map(place =>
<likey={place.id}>
<Place
place={place}
imageSize={imageSize}
/>
</li>
);
return <ul>{listItems}</ul>;
}
function Profile({ person, thumbnailSize }) {
function Place({ place, imageSize }) {
return (
<>
<Avatar
person={person}
thumbnailSize={thumbnailSize}
<PlaceImage
place={place}
imageSize={imageSize}
/>
<p>
<b>{person.name}</b>
{' ' + person.profession + ' '}
known for {person.accomplishment}
<b>{place.name}</b>
{': ' + place.description}
</p>
</>
);
}
function Avatar({ person, thumbnailSize }) {
function PlaceImage({ place, imageSize }) {
return (
<img
className="avatar"
src={getImageUrl(person)}
alt={person.name}
width={thumbnailSize}
height={thumbnailSize}
src={getImageUrl(place)}
alt={place.name}
width={imageSize}
height={imageSize}
/>
);
}
@ -933,45 +926,50 @@ function Avatar({ person, thumbnailSize }) {
```
```js data.js
export const people = [{
id: 0, // Used in JSX as a key
name: 'Creola Katherine Johnson',
profession: 'mathematician',
accomplishment: 'spaceflight calculations',
imageId: 'MK3eW3A'
export const places = [{
id: 0,
name: 'Bo-Kaap in Cape Town, South Africa',
description: 'The tradition of choosing bright colors for houses began in the late 20th century.',
imageId: 'K9HVAGH'
}, {
id: 1,
name: 'Rainbow Village in Taichung, Taiwan',
description: 'To save the houses from demolition, Huang Yung-Fu, a local resident, painted all 1,200 of them in 1924.',
imageId: '9EAYZrt'
}, {
id: 1, // Used in JSX as a key
name: 'Mario José Molina-Pasquel Henríquez',
profession: 'chemist',
accomplishment: 'discovery of Arctic ozone hole',
imageId: 'mynHUSa'
id: 2,
name: 'Macromural de Pachuca, Mexico',
description: 'One of the largest murals in the world covering homes in a hillside neighborhood.',
imageId: 'DgXHVwu'
}, {
id: 2, // Used in JSX as a key
name: 'Mohammad Abdus Salam',
profession: 'physicist',
accomplishment: 'electromagnetism theory',
imageId: 'bE7W1ji'
id: 3,
name: 'Selarón Staircase in Rio de Janeiro, Brazil',
description: 'This landmark was created by Jorge Selarón, a Chilean-born artist, as a "tribute to the Brazilian people."',
imageId: 'aeO3rpI'
}, {
id: 3, // Used in JSX as a key
name: 'Percy Lavon Julian',
profession: 'chemist',
accomplishment: 'pioneering cortisone drugs, steroids and birth control pills',
imageId: 'IOjWm71'
id: 4,
name: 'Burano, Italy',
description: 'The houses are painted following a specific color system dating back to 16th century.',
imageId: 'kxsph5C'
}, {
id: 4, // Used in JSX as a key
name: 'Subrahmanyan Chandrasekhar',
profession: 'astrophysicist',
accomplishment: 'white dwarf star mass calculations',
imageId: 'lrWQx8l'
id: 5,
name: 'Chefchaouen, Marocco',
description: 'There are a few theories on why the houses are painted blue, including that the color repells mosquitos or that it symbolizes sky and heaven.',
imageId: 'rTqKo46'
}, {
id: 6,
name: 'Gamcheon Culture Village in Busan, South Korea',
description: 'In 2009, the village was converted into a cultural hub by painting the houses and featuring exhibitions and art installations.',
imageId: 'ZfQOOzf'
}];
```
```js utils.js
export function getImageUrl(person) {
export function getImageUrl(place) {
return (
'https://i.imgur.com/' +
person.imageId +
's.jpg'
place.imageId +
'l.jpg'
);
}
```
@ -985,82 +983,76 @@ li {
gap: 20px;
align-items: center;
}
img { border-radius: 50%; }
```
</Sandpack>
<Solution>
Remove `thumbnailSize` prop from all the components.
Remove `imageSize` prop from all the components.
Create and export `ThumbnailSizeContext` from `Context.js`. Then wrap the List into `<ThumbnailSizeContext.Provider value={thumbnailSize}>` to pass the value down, and `useContext(ThumbnailSizeContext)` to read it in the `Avatar`:
Create and export `ImageSizeContext` from `Context.js`. Then wrap the List into `<ImageSizeContext.Provider value={imageSize}>` to pass the value down, and `useContext(ImageSizeContext)` to read it in the `PlaceImage`:
<Sandpack>
```js App.js
import { useState, useContext } from 'react';
import { people } from './data.js';
import { places } from './data.js';
import { getImageUrl } from './utils.js';
import { ThumbnailSizeContext } from './Context.js';
description: 'The tradition of choosing bright colors for houses began in the late 20th century.',
imageId: 'K9HVAGH'
}, {
id: 1,
name: 'Rainbow Village in Taichung, Taiwan',
description: 'To save the houses from demolition, Huang Yung-Fu, a local resident, painted all 1,200 of them in 1924.',
imageId: '9EAYZrt'
}, {
id: 2,
name: 'Macromural de Pachuca, Mexico',
description: 'One of the largest murals in the world covering homes in a hillside neighborhood.',
imageId: 'DgXHVwu'
}, {
id: 1, // Used in JSX as a key
name: 'Mario José Molina-Pasquel Henríquez',
profession: 'chemist',
accomplishment: 'discovery of Arctic ozone hole',
imageId: 'mynHUSa'
id: 3,
name: 'Selarón Staircase in Rio de Janeiro, Brazil',
description: 'This landmark was created by Jorge Selarón, a Chilean-born artist, as a "tribute to the Brazilian people."',
imageId: 'aeO3rpI'
}, {
id: 2, // Used in JSX as a key
name: 'Mohammad Abdus Salam',
profession: 'physicist',
accomplishment: 'electromagnetism theory',
imageId: 'bE7W1ji'
id: 4,
name: 'Burano, Italy',
description: 'The houses are painted following a specific color system dating back to 16th century.',
imageId: 'kxsph5C'
}, {
id: 3, // Used in JSX as a key
name: 'Percy Lavon Julian',
profession: 'chemist',
accomplishment: 'pioneering cortisone drugs, steroids and birth control pills',
imageId: 'IOjWm71'
id: 5,
name: 'Chefchaouen, Marocco',
description: 'There are a few theories on why the houses are painted blue, including that the color repells mosquitos or that it symbolizes sky and heaven.',
imageId: 'rTqKo46'
}, {
id: 4, // Used in JSX as a key
name: 'Subrahmanyan Chandrasekhar',
profession: 'astrophysicist',
accomplishment: 'white dwarf star mass calculations',
imageId: 'lrWQx8l'
id: 6,
name: 'Gamcheon Culture Village in Busan, South Korea',
description: 'In 2009, the village was converted into a cultural hub by painting the houses and featuring exhibitions and art installations.',
imageId: 'ZfQOOzf'
}];
```
```js utils.js
export function getImageUrl(person) {
export function getImageUrl(place) {
return (
'https://i.imgur.com/' +
person.imageId +
's.jpg'
place.imageId +
'l.jpg'
);
}
```
@ -1125,12 +1122,11 @@ li {
gap: 20px;
align-items: center;
}
img { border-radius: 50%; }
```
</Sandpack>
Note how components in the middle don't need to pass `thumbnailSize` anymore.
Note how components in the middle don't need to pass `imageSize` anymore.
@ -4,7 +4,7 @@ title: Preserving and Resetting State
<Intro>
State is isolated between components. React keeps track of which state belongs to which component based on their place in the tree. You can control when to preserve state and when to reset it between re-renders.
State is isolated between components. React keeps track of which state belongs to which component based on their place in the UI tree. You can control when to preserve state and when to reset it between re-renders.
</Intro>
@ -234,7 +234,7 @@ label {
</Sandpack>
Notice how the moment you stop rendering the second counter, its state disappears completely. That's because when React removes a component, it destroys state.
Notice how the moment you stop rendering the second counter, its state disappears completely. That's because when React removes a component, it destroys its state.
<imgalt="React removes a component from the tree, it destroys its state as well"src="/images/docs/sketches/s_remove-ui.png"/>
@ -342,9 +342,7 @@ It's the same component at the same position, so from React's perspective, it's
<Gotcha>
Remember that **it's the position in the UI tree--not in the JSX markup--that matters to React!**
This component has two `return` clauses with different `<Counter />` JSX tags inside and outside the `if`:
Remember that **it's the position in the UI tree--not in the JSX markup--that matters to React!** This component has two `return` clauses with different `<Counter />` JSX tags inside and outside the `if`:
<Sandpack>
@ -442,7 +440,7 @@ label {
</Sandpack>
You might expect the state to be reset when you tick checkbox, but it doesn't! This is because **both of these `<Counter />` tags are rendered at the same position.** React doesn't know where you place the conditions in your function. All it "sees" is the tree you return. In both cases, the `App` component returns a `<div>` with `<Counter />` as a first child. This is why React considers them to represent_the same_`<Counter />`.
You might expect the state to reset when you tick checkbox, but it doesn't! This is because **both of these `<Counter />` tags are rendered at the same position.** React doesn't know where you place the conditions in your function. All it "sees" is the tree you return. In both cases, the `App` component returns a `<div>` with `<Counter />` as a first child. This is why React considers them as_the same_`<Counter />`.
You can think of them as having the same "address": the first child of the first child of the root. This is how React matches them up between the previous and next renders, regardless of how you structure your logic.
@ -620,7 +618,7 @@ label {
</Sandpack>
The counter state gets reset when you click the checkbox. Although you render a `Counter`, the first child of the `div` changes from a `div` to a `section`. When `div` was removed from the DOM, the whole tree below it (including the `Counter` and its state) was destroyed as well.
The counter state gets reset when you click the checkbox. Although you render a `Counter`, the first child of the `div` changes from a `div` to a `section`. When the child `div` was removed from the DOM, the whole tree below it (including the `Counter` and its state) was destroyed as well.
<imgalt="If the first child isn't the same, forget about it!"src="/images/docs/sketches/s_ui-components-swap.png"/>
@ -921,7 +919,7 @@ Switching between Taylor and Sarah does not preserve the state. This is because
)}
```
Specifying a `key` tells React to use the `key` itself as part of the position, instead of their order within the parent. This is why, even though you render them in the same place in JSX, from React's perspective, these are two different counters. As a result, they will never share state. Every time a counter appears on screen, its state is created. Every time it is removed, its state is destroyed. Toggling between them resets their state over and over.
Specifying a `key` tells React to use the `key` itself as part of the position, instead of their order within the parent. This is why, even though you render them in the same place in JSX, from React's perspective, these are two different counters. As a result, they will never share state. Every time a counter appears on the screen, its state is created. Every time it is removed, its state is destroyed. Toggling between them resets their state over and over.
<Illustrationsrc="/images/docs/illustrations/i_keys-in-trees.png"alt="React distinguishes between components with different keys, even if they are of the same type."/>
@ -1170,7 +1168,7 @@ export default function App() {
if (showHint) {
return (
<div>
<p><i>Hint: Your favorite movie?</i></p>
<p><i>Hint: Your favorite city?</i></p>
<Form/>
<buttononClick={()=> {
setShowHint(false);
@ -1221,7 +1219,7 @@ export default function App() {
return (
<div>
{showHint &&
<p><i>Hint: Your favorite movie?</i></p>
<p><i>Hint: Your favorite city?</i></p>
}
<Form/>
{showHint ? (
@ -1267,7 +1265,7 @@ export default function App() {
if (showHint) {
return (
<div>
<p><i>Hint: Your favorite movie?</i></p>
<p><i>Hint: Your favorite city?</i></p>
<Form/>
<buttononClick={()=> {
setShowHint(false);
@ -1752,7 +1750,7 @@ button {
### Clear an image while it's loading
When you press "Next", the browser starts loading the next image. However, because it's displayed in the same `<img>` tag, by default the browser will keep showing the previous image until the next one loads. This may be undesirable if it's important for the text to always match the image. Change it so that the moment you press "Next," the previous image immediately clears.
When you press "Next", the browser starts loading the next image. However, because it's displayed in the same `<img>` tag, by default you would still see the previous image until the next one loads. This may be undesirable if it's important for the text to always match the image. Change it so that the moment you press "Next," the previous image immediately clears.
<Hint>
@ -1765,7 +1763,7 @@ Is there a way to tell React to re-create the DOM instead of reusing it?
```js
import { useState } from 'react';
export default function ReviewTool() {
export default function Gallery() {
const [index, setIndex] = useState(0);
const hasNext = index <images.length-1;
@ -1777,7 +1775,7 @@ export default function ReviewTool() {
}
}
let src = images[index];
let image = images[index];
return (
<>
<buttononClick={handleClick}>
@ -1786,21 +1784,40 @@ export default function ReviewTool() {
<h3>
Image {index + 1} of {images.length}
</h3>
<imgsrc={src}/>
<imgsrc={image.src}/>
<p>
{image.place}
</p>
</>
);
}
let images = [
'https://placekitten.com/100?image=1',
'https://placekitten.com/100?image=2',
'https://placekitten.com/100?image=3',
'https://placekitten.com/100?image=4',
'https://placekitten.com/100?image=5',
'https://placekitten.com/100?image=6',
'https://placekitten.com/100?image=7',
'https://placekitten.com/100?image=8',
];
let images = [{
place: 'Penang, Malaysia',
src: 'https://i.imgur.com/FJeJR8M.jpg'
}, {
place: 'Lisbon, Portugal',
src: 'https://i.imgur.com/dB2LRbj.jpg'
}, {
place: 'Bilbao, Spain',
src: 'https://i.imgur.com/z08o2TS.jpg'
}, {
place: 'Valparaíso, Chile',
src: 'https://i.imgur.com/Y3utgTi.jpg'
}, {
place: 'Schwyz, Switzerland',
src: 'https://i.imgur.com/JBbMpWY.jpg'
}, {
place: 'Prague, Czechia',
src: 'https://i.imgur.com/QwUKKmF.jpg'
}, {
place: 'Ljubljana, Slovenia',
src: 'https://i.imgur.com/3aIiwfm.jpg'
}];
```
```css
img { width: 150px; height: 150px; }
```
</Sandpack>
@ -1814,7 +1831,7 @@ You can provide a `key` to the `<img>` tag. When that `key` changes, React will
```js
import { useState } from 'react';
export default function ReviewTool() {
export default function Gallery() {
const [index, setIndex] = useState(0);
const hasNext = index <images.length-1;
@ -1826,7 +1843,7 @@ export default function ReviewTool() {
}
}
let src = images[index];
let image = images[index];
return (
<>
<buttononClick={handleClick}>
@ -1835,21 +1852,40 @@ export default function ReviewTool() {
@ -4,7 +4,7 @@ title: Reacting to Input with State
<Intro>
React uses a declarative model for manipulating UI. This means you describe the different states your component can be in according to different inputs rather than manipulating individual pieces of the UI in response to inputs. This is similar to how designers think about UI.
React uses a declarative way to manipulate the UI. Instead of manipulating individual pieces of the UI directly, you describe the different states that your component can be in, and switch between them in response to the user input. This is similar to how designers think about the UI.
</Intro>
@ -18,18 +18,20 @@ React uses a declarative model for manipulating UI. This means you describe the
## How declarative UI compares to imperative
When you design UI interactions, you probably think about how the UI *changes* in response to user actions. Consider a form that lets the user submit some feedback:
When you design UI interactions, you probably think about how the UI *changes* in response to user actions. Consider a form that lets the user submit an answer:
* When you type something into a form, the "Submit" button **becomes enabled**.
* When you press "Submit", both form and the button **become disabled**, and a spinner **appears**.
* If the network request succeeds, the form **gets hidden**, and the "Thank you" message **appears**.
* If the network request fails, an error message **appears**, and the form **becomes enabled** again.
In **imperative programming**, the above corresponds directly to how you implement interaction. You have to write the exact instructions to manipulate the UI depending on what just happened. Another way to think of this: imagine riding next to someone in a car and telling them turn by turn where to go. They don't know where you want to go, they just follow your commands. (And if you get the directions wrong, you end up in the wrong place!) It's called *imperative* because you have to "command" each element, from the spinner to the button, telling the computer *how* to update the UI.
In **imperative programming**, the above corresponds directly to how you implement interaction. You have to write the exact instructions to manipulate the UI depending on what just happened. Here's another way to think about this: imagine riding next to someone in a car and telling them turn by turn where to go.
<Illustrationsrc="/images/docs/illustrations/i_imperative-ui-programming.png"alt="In a car driven by an anxious-looking person representing JavaScript, a passenger orders the driver to execute a sequence of complicated turn by turn navigations."/>
In this example of imperative UI programming, the form is built *without* React. It's made with the built-in browser [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model):
They don't know where you want to go, they just follow your commands. (And if you get the directions wrong, you end up in the wrong place!) It's called *imperative* because you have to "command" each element, from the spinner to the button, telling the computer *how* to update the UI.
In this example of imperative UI programming, the form is built *without* React. It uses the built-in browser [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model):
<Sandpack>
@ -41,7 +43,7 @@ async function handleFormSubmit(e) {
show(loadingMessage);
hide(errorMessage);
try {
await submitForm();
await submitForm(textarea.value);
show(successMessage);
hide(form);
} catch (err) {
@ -78,15 +80,14 @@ function disable(el) {
el.disabled = true;
}
function submitForm() {
function submitForm(answer) {
// Pretend it's hitting the network.
return new Promise((resolve, reject) => {
setTimeout(() => {
let shouldError = Math.random() > 0.5;
if (shouldError) {
reject(new Error('Something went wrong'));
} else {
if (answer.toLowerCase() == 'istanbul') {
resolve();
} else {
reject(new Error('Good guess but a wrong answer. Try again!'));
body { font-family: sans-serif; margin: 20px; padding: 0; }
</style>
```
</Sandpack>
Manipulating the UI works well enough for isolated examples, but it gets exponentially more difficult to manage in more complex systems. Imagine updating a page full of different forms like this one. Adding a new UI element or a new interaction would require carefully checking all existing code to make sure you haven't introduced a bug (for example, forgetting to show or hide something).
Manipulating the UI imperatively works well enough for isolated examples, but it gets exponentially more difficult to manage in more complex systems. Imagine updating a page full of different forms like this one. Adding a new UI element or a new interaction would require carefully checking all existing code to make sure you haven't introduced a bug (for example, forgetting to show or hide something).
React was built to solve this problem.
In React, you don't directly manipulate the UI--meaning you don't enable, disable, show, or hide components directly. Instead, you **declare what you want to show**, and React figures out how to update the UI. Think of getting into a taxi and telling the driver where you want to go instead of telling them exactly where to turn. It's the driver's job to get you there, and they might even know some shortcuts you hadn't considered!
In React, you don't directly manipulate the UI--meaning you don't enable, disable, show, or hide components directly. Instead, you **declare what you want to show**, and React figures out how to update the UI. Think of getting into a taxi and telling the driver where you want to go instead of telling them exactly where to turn. It's the driver's job to get you there, and they might even know some shortcuts you haven't considered!
<Illustrationsrc="/images/docs/illustrations/i_declarative-ui-programming.png"alt="In a car driven by React, a passenger asks to be taken to a specific place on the map. React figures out how to do that."/>
@ -141,7 +151,7 @@ You've seen how to implement a form imperatively above. To better understand how
### Step 1: Identify your component's different visual states
In computer science, you may hear about a ["state machine"](https://en.wikipedia.org/wiki/Finite-state_machine) being in one of several “states”. If you work with a designer, you may have seen visual mockups for different states. Designers work with visual states all the time. React stands at the intersection of design and computer science, so both of these ideas are sources of inspiration.
In computer science, you may hear about a ["state machine"](https://en.wikipedia.org/wiki/Finite-state_machine) being in one of several “states”. If you work with a designer, you may have seen mockups for different "visual states". React stands at the intersection of design and computer science, so both of these ideas are sources of inspiration.
First, you need to visualize all the different "states" of the UI the user might see:
@ -156,58 +166,68 @@ Just like a designer, you'll want to "mock up" or create "mocks" for the differe
<Sandpack>
```js
export default function FeedbackForm({
export default function Form({
status = 'empty'
}) {
if (status === 'success') {
return <h1>Thank you!</h1>
return <h1>That's right!</h1>
}
return (
<form>
<textarea/>
<br/>
<button>
Submit
</button>
</form>
<>
<h2>City quiz</h2>
<p>
In which city is there a billboard that turns air into drinkable water?
</p>
<form>
<textarea/>
<br/>
<button>
Submit
</button>
</form>
</>
)
}
```
</Sandpack>
You could call that prop anything you like, the naming is not important. Try editing `status = 'empty'` to `status = 'success'` to see the success message appear. Mocking lets you quickly iterate on the UI before you wire up any logic.
Here is a more fleshed out prototype of the same component, still "controlled" by the `status` prop:
You could call that prop anything you like, the naming is not important. Try editing `status = 'empty'` to `status = 'success'` to see the success message appear. Mocking lets you quickly iterate on the UI before you wire up any logic. Here is a more fleshed out prototype of the same component, still "controlled" by the `status` prop:
<Sandpack>
```js
export default function FeedbackForm({
export default function Form({
// Try 'submitting', 'error', 'success':
status = 'empty'
}) {
if (status === 'success') {
return <h1>Thank you!</h1>
return <h1>That's right!</h1>
}
return (
<form>
<textareadisabled={
status === 'submitting'
} />
<br/>
<buttondisabled={
status === 'empty' ||
status === 'submitting'
}>
Submit
</button>
{status === 'error' &&
<pclassName="Error">
Something went wrong
</p>
}
</form>
<>
<h2>City quiz</h2>
<p>
In which city is there a billboard that turns air into drinkable water?
</p>
<form>
<textareadisabled={
status === 'submitting'
} />
<br/>
<buttondisabled={
status === 'empty' ||
status === 'submitting'
}>
Submit
</button>
{status === 'error' &&
<pclassName="Error">
Good guess but a wrong answer. Try again!
</p>
}
</form>
</>
);
}
```
@ -225,7 +245,7 @@ If a component has a lot of visual states, it can be convenient to show them all
<Sandpack>
```js App.js active
import FeedbackForm from './FeedbackForm.js';
import Form from './Form.js';
let statuses = [
'empty',
@ -240,8 +260,8 @@ export default function App() {
<>
{statuses.map(status => (
<sectionkey={status}>
<h4>FeedbackForm ({status}):</h4>
<FeedbackFormstatus={status}/>
<h4>Form ({status}):</h4>
<Formstatus={status}/>
</section>
))}
</>
@ -249,10 +269,10 @@ export default function App() {
}
```
```js FeedbackForm.js
export default function FeedbackForm({ status }) {
```js Form.js
export default function Form({ status }) {
if (status === 'success') {
return <h1>Thank you!</h1>
return <h1>That's right!</h1>
}
return (
<form>
@ -268,7 +288,7 @@ export default function FeedbackForm({ status }) {
</button>
{status === 'error' &&
<pclassName="Error">
Something went wrong
Good guess but a wrong answer. Try again!
</p>
}
</form>
@ -285,7 +305,7 @@ body { margin: 0; }
</Sandpack>
Pages like this are often known as "living styleguides" or "storybooks."
Pages like this are often called "living styleguides" or "storybooks."
</DeepDive>
@ -296,17 +316,17 @@ Pages like this are often known as "living styleguides" or "storybooks."
<Illustrationcaption="Computer inputs"alt="Ones and zeroes."src="/images/docs/illustrations/i_inputs2.png"/>
</IllustrationBlock>
State changes can generally be triggered from two different sources:
You can trigger state updates in response to two kinds of inputs:
* **Human** triggers like clicking a button, typing in a field, navigating a link.
* **Computer** triggers like a network response arriving, a timeout completing, an image loading.
* **Human inputs**, like clicking a button, typing in a field, navigating a link.
* **Computer inputs,** like a network response arriving, a timeout completing, an image loading.
**All triggers must set [state variables](/learn/state-a-components-memory#anatomy-of-usestate) to update the UI.** For the form you're developing, the following triggers will need to change state:
In both cases, **you must set [state variables](/learn/state-a-components-memory#anatomy-of-usestate) to update the UI**. For the form you're developing, you will need to change state in response to a few different inputs:
* **Changing the text input** (human) should switch it from the Empty state to the Typing state or back, depending on whether the text box is empty or not.
* **Clicking the Submit button** (human) should switch it to the Submitting state.
* **Successful network response** (computer) should switch it to the Success state.
* **Failed network response** (computer) should switch it to the Error state with the corresponding error message.
* **Changing the text input** (human) should switch it from the *Empty* state to the *Typing* state or back, depending on whether the text box is empty or not.
* **Clicking the Submit button** (human) should switch it to the *Submitting* state.
* **Successful network response** (computer) should switch it to the *Success* state.
* **Failed network response** (computer) should switch it to the *Error* state with the matching error message.
> Notice that human inputs often require [event handlers](/learn/responding-to-events)!
@ -314,18 +334,18 @@ To help visualize this flow, try drawing each state on paper as a labeled circle
<imgalt="A flow chart showing states and transitions between them"src="/images/docs/sketches/s_flow-chart.jpg"/>
### Step 3: Represent the state in memory using`useState`
### Step 3: Represent the state in memory with`useState`
Next you'll need to represent the visual states of your component in memory using `useState`. Simplicity is key: each piece of state is a "moving piece", and **you want as few "moving pieces" as possible**. More complexity leads to more bugs!
Next you'll need to represent the visual states of your component in memory with [`useState`](/reference/usestate). Simplicity is key: each piece of state is a "moving piece", and **you want as few "moving pieces" as possible**. More complexity leads to more bugs!
Start with the state that *absolutely must* be there. For example, you'll need to store the `message` for the input, and the `error` (if it exists) to store the last error:
Start with the state that *absolutely must* be there. For example, you'll need to store the `answer` for the input, and the `error` (if it exists) to store the last error:
```js
const [message, setMessage] = useState('');
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
```
Then, you need to store which of the visual states described earlier you want to display. There's usually more than a single way to represent that in memory, so you'll need to experiment with it.
Then, you'll need a state variable representing which one of the visual states described earlier you want to display. There's usually more than a single way to represent that in memory, so you'll need to experiment with it.
If you struggle to think of the best way immediately, start by adding enough state that you're *definitely* sure that all the possible visual states are covered:
Your first idea likely won't be the best, but that's ok—refactoring state is a part of the process!
Your first idea likely won't be the best, but that's ok--refactoring state is a part of the process!
### Step 4: Remove any non-essential state variables
You want to avoid duplication in the state content so you're only tracking what is essential. Spending a little time on refactoring your state structure will make your components easier to understand, reduce duplication, and avoid unintended meanings. Your goal is to **prevent the cases where the state in memory doesn't represent any valid UI that you'd want a user to see.** (Like an error message with a disabled input preventing the user from correcting the error!)
You want to avoid duplication in the state content so you're only tracking what is essential. Spending a little time on refactoring your state structure will make your components easier to understand, reduce duplication, and avoid unintended meanings. Your goal is to **prevent the cases where the state in memory doesn't represent any valid UI that you'd want a user to see.** (For example, you never want to show an error message and disable the input at the same time, or the user won't be able to correct the error!)
Here are some questions you can ask your state variables:
Here are some questions you can ask about your state variables:
* **Does this state cause a paradox?** For example, `isTyping` and `isSubmitting` can't both be `true`. A paradox usually means that the state is not constrained enough. There are four possible combinations of two booleans, but only three correspond to valid states. To remove the "impossible" state, you can combine these into a `status` that must be one of three values: `'typing'`, `'submitting'`, or `'success'`.
* **Is the same information available in another state variable already?** Another paradox: `isEmpty` and `isTyping` can't be `true` at the same time. By making them separate state variables, you're risking that you'll have a bug where they go out of sync. Fortunately, you can remove `isEmpty` and instead check `message.length === 0`.
* **Is the same information available in another state variable already?** Another paradox: `isEmpty` and `isTyping` can't be `true` at the same time. By making them separate state variables, you risk them going out of sync and causing bugs. Fortunately, you can remove `isEmpty` and instead check `message.length === 0`.
* **Can you get the same information from the inverse of another state variable?**`isError` is not needed because you can check `error !== null` instead.
After this clean-up, you're left with 3 (down from 7!) *essential* state variables:
```js
const [message, setMessage] = useState('');
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
const [status, setStatus] = useState('typing'); // 'typing', 'submitting', or 'success'
```
You know they're essential, because you can't remove any of them without breaking the functionality.
You know they are essential, because you can't remove any of them without breaking the functionality.
<DeepDivetitle="Eliminating “impossible” states with a reducer">
These three variables are a good enough representation of this form's state. However, if we're being pedantic, there are still some intermediate states that don't fully make sense (for example, an `error` only makes sense when `status` is `'typing'`). If you want to model the state even more strictly, consider [extracting it into a reducer](/learn/extracting-state-logic-into-a-reducer) which lets you unify multiple state variables and consolidate the related logic. More on that in the following pages!
These three variables are a good enough representation of this form's state. However, there are still some intermediate states that don't fully make sense. For example, a non-null `error` doesn't make sense when `status` is `'success'`. To model the state more precisely, you can [extract it into a reducer](/learn/extracting-state-logic-into-a-reducer). Reducers let you unify multiple state variables into a single object and consolidate all the related logic!
</DeepDive>
### Step 5: Connect the event handlers to set the state
### Step 5: Connect the event handlers to set state
Lastly, create event handlers to set the state variables. Below is the final form in React, with all event handlers wired up:
Lastly, create event handlers to set the state variables. Below is the final form, with all event handlers wired up:
<Sandpack>
```js
import { useState } from 'react';
export default function FeedbackForm() {
const [message, setMessage] = useState('');
export default function Form() {
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
const [status, setStatus] = useState('typing');
if (status === 'success') {
return <h1>Thank you!</h1>
return <h1>That's right!</h1>
}
async function handleSubmit(e) {
e.preventDefault();
setStatus('submitting');
try {
await submitForm();
await submitForm(answer);
setStatus('success');
} catch (err) {
setStatus('typing');
@ -396,39 +416,45 @@ export default function FeedbackForm() {
}
function handleTextareaChange(e) {
setMessage(e.target.value);
setAnswer(e.target.value);
}
return (
<formonSubmit={handleSubmit}>
<textarea
value={message}
onChange={handleTextareaChange}
disabled={status === 'submitting'}
/>
<br/>
<buttondisabled={
message.length === 0 ||
status === 'submitting'
}>
Submit
</button>
{error !== null &&
<pclassName="Error">
{error.message}
</p>
}
</form>
<>
<h2>City quiz</h2>
<p>
In which city is there a billboard that turns air into drinkable water?
</p>
<formonSubmit={handleSubmit}>
<textarea
value={answer}
onChange={handleTextareaChange}
disabled={status === 'submitting'}
/>
<br/>
<buttondisabled={
answer.length === 0 ||
status === 'submitting'
}>
Submit
</button>
{error !== null &&
<pclassName="Error">
{error.message}
</p>
}
</form>
</>
);
}
function submitForm() {
function submitForm(answer) {
// Pretend it's hitting the network.
return new Promise((resolve, reject) => {
setTimeout(() => {
let shouldError = Math.random() > 0.5;
let shouldError = answer.toLowerCase() !== 'lima'
if (shouldError) {
reject(new Error('Something went wrong'));
reject(new Error('Good guess but a wrong answer. Try again!'));
} else {
resolve();
}
@ -453,7 +479,7 @@ Although this code is longer than the original imperative example, it is much le
2. Determine the human and computer triggers for state changes.
3. Model the state with `useState`.
4. Remove non-essential state to avoid bugs and paradoxes.
5. Connect the event handlers to set the state.
5. Connect the event handlers to set state.
</Recap>
@ -463,20 +489,20 @@ Although this code is longer than the original imperative example, it is much le
### Add and remove a CSS class
Make it so that clicking on the kitten*removes* the `background--active` class from the outer div, but *adds* the `kitten--active` class to the image. Clicking the background again should restore the original CSS classes.
Make it so that clicking on the picture*removes* the `background--active`CSS class from the outer `<div>`, but *adds* the `picture--active` class to the `<img>`. Clicking the background again should restore the original CSS classes.
Visually, you should expect that clicking on the image removes the yellow background and highlights the image border. Clicking outside the image highlights the background, but removes the image border highlight.
Visually, you should expect that clicking on the picture removes the purple background and highlights the picture border. Clicking outside the picture highlights the background, but removes the picture border highlight.
<Sandpack>
```js
export default function Kitten() {
export default function Picture() {
return (
<divclassName="background background--active">
<img
className="kitten"
alt="Kitten"
src="https://placekitten.com/100"
className="picture"
alt="Rainbow houses in Kampung Pelangi, Indonesia"
This component has two visual states: when the image is active, and when the image is inactive:
* When the image is active, the CSS classes are `background` and `kitten kitten--active`.
* When the image is inactive, the CSS classes are `background background--active` and `kitten`.
* When the image is active, the CSS classes are `background` and `picture picture--active`.
* When the image is inactive, the CSS classes are `background background--active` and `picture`.
A single boolean state variable is enough to remember whether the image is active. The original task was to remove or add CSS classes. However, in React you need to *describe* what you want to see rather than *manipulate* the UI elements. So you need to calculate both CSS classes based on the current state. You also need to [stop the propagation](/learn/responding-to-events#stopping-propagation) so that clicking the image doesn't register as a click on the background.
@ -528,13 +554,13 @@ Verify that this version works by clicking the image and then outside of it:
```js
import { useState } from 'react';
export default function Kitten() {
export default function Picture() {
const [isActive, setIsActive] = useState(false);
let backgroundClassName = 'background';
let kittenClassName = 'kitten';
let pictureClassName = 'picture';
if (isActive) {
kittenClassName += ' kitten--active';
pictureClassName += ' picture--active';
} else {
backgroundClassName += ' background--active';
}
@ -549,9 +575,9 @@ export default function Kitten() {
e.stopPropagation();
setIsActive(true);
}}
className={kittenClassName}
alt="Kitten"
src="https://placekitten.com/100"
className={pictureClassName}
alt="Rainbow houses in Kampung Pelangi, Indonesia"
@ -228,15 +229,19 @@ And `TaskList` passes the event handlers to `Task`:
/>
```
In a small example like this, this works well, but if you have tens or hundreds of components in the middle, passing down all state and functions to change through every can be quite frustrating!
In a small example like this, this works well, but if you have tens or hundreds of components in the middle, passing down all state and functions can be quite frustrating!
<!--(TODO: illustration of prop drilling)-->
This is why, as an alternative to passing them through props, you might want to put both the `tasks` state and the `dispatch` function [into context](/learn/passing-data-deeply-with-context). **This way, any components below `TaskBoard` in the tree can read the tasks and dispatch actions anywhere in the tree without the repetitive "prop drilling".**
This is why, as an alternative to passing them through props, you might want to put both the `tasks` state and the `dispatch` function [into context](/learn/passing-data-deeply-with-context). **This way, any component below `TaskBoard` in the tree can read the tasks and dispatch actions without the repetitive "prop drilling".**
<!--(TODO: illustration of context)-->
Here is how you can combine a reducer with context.
Here is how you can combine a reducer with context:
1. **Create** the context.
2. **Put** state and dispatch into context.
3. **Use** context anywhere in the tree.
### Step 1: Create the context
@ -289,7 +294,8 @@ export default function TaskBoard() {
}
return (
<>
<>
<h1>Day off in Kyoto</h1>
<AddTask
onAddTask={handleAddTask}
/>
@ -331,17 +337,16 @@ function tasksReducer(tasks, action) {
@ -453,11 +458,8 @@ Here, you're passing `null` as the default value to both contexts. The actual va
Now you can import both contexts in your `TaskBoard` component. Take the `tasks` and `dispatch` returned by `useReducer()` and [provide them](/learn/passing-data-deeply-with-context#step-3-provide-the-context) to the entire tree below:
```js {7,10-11}
import {
TasksContext,
TasksDispatchContext
} from './TaskBoardContext.js';
```js {4,7-8}
import { TasksContext, TasksDispatchContext } from './TasksContext.js';
You don't have to do this, but you could further declutter the components by moving both reducer and context into a single file. Currently, `TaskBoardContext.js` contains only two context declarations:
You don't have to do this, but you could further declutter the components by moving both reducer and context into a single file. Currently, `TasksContext.js` contains only two context declarations:
You can also export functions that _use_ the context from `TaskBoardContext.js`:
You can also export functions that _use_ the context from `TasksContext.js`:
```js
export function useTasks() {
@ -1163,18 +1144,19 @@ const tasks = useTasks();
const dispatch = useTasksDispatch();
```
This doesn't change the behavior in any way, but it lets you later split these contexts further or add some logic to these functions. **Now all of the context and reducer wiring is in `TaskBoardContext.js`. This keeps the components clean and uncluttered, focused on what they display rather than where they get the data:**
This doesn't change the behavior in any way, but it lets you later split these contexts further or add some logic to these functions. **Now all of the context and reducer wiring is in `TasksContext.js`. This keeps the components clean and uncluttered, focused on what they display rather than where they get the data:**
<Sandpack>
```js App.js
import AddTask from './AddTask.js';
import TaskList from './TaskList.js';
import { TasksProvider } from './TaskBoardContext.js';
import { TasksProvider } from './TasksContext.js';
export default function TaskBoard() {
return (
<TasksProvider>
<h1>Day off in Kyoto</h1>
<AddTask/>
<TaskList/>
</TasksProvider>
@ -1182,12 +1164,8 @@ export default function TaskBoard() {
}
```
```js TaskBoardContext.js
import {
createContext,
useContext,
useReducer
} from 'react';
```js TasksContext.js
import { createContext, useContext, useReducer } from 'react';
const TasksContext = createContext(null);
@ -1201,9 +1179,7 @@ export function TasksProvider({ children }) {
return (
<TasksContext.Providervalue={tasks}>
<TasksDispatchContext.Provider
value={dispatch}
>
<TasksDispatchContext.Providervalue={dispatch}>
{children}
</TasksDispatchContext.Provider>
</TasksContext.Provider>
@ -1246,15 +1222,15 @@ function tasksReducer(tasks, action) {
You can think of `TasksProvider` as a part of the screen that knows how to deal with tasks, `useTasks` as a way to read them, and `useTasksDispatch` as a way to update them from any components below in the tree.
You can think of `TasksProvider` as a part of the screen that knows how to deal with tasks, `useTasks` as a way to read them, and `useTasksDispatch` as a way to update them from any component below in the tree.
> Functions like `useTasks` and `useTasksDispatch` are called **[Custom Hooks](/learn/reusing-logic-with-custom-hooks).** Your function is considered a custom Hook if its name starts with `use`. This lets you use other Hooks, like `useContext`, inside it.
As your app grows, you may have many context-reducer pairs like this. This is a powerful way to scale your app and [lift state up](/learn/sharing-state-between-components) without too much ceremony whenever you want to access the data deep in the tree.
As your app grows, you may have many context-reducer pairs like this. This is a powerful way to scale your app and [lift state up](/learn/sharing-state-between-components) without too much work whenever you want to access the data deep in the tree.
@ -17,9 +17,13 @@ Sometimes, you want the state of two components to always change together. To do
## Lifting state up by example
In this example, there are two components.
In this example, a parent `Accordion` component renders two separate `Panel`s:
There is a parent `Accordion` component that renders two separate `Panel`s. Each `Panel` component has a boolean `isActive` state that determines whether its content is visible.
* `Accordion`
- `Panel`
- `Panel`
Each `Panel` component has a boolean `isActive` state that determines whether its content is visible.
Press the Show button for both panels:
@ -47,12 +51,12 @@ function Panel({ title, children }) {
export default function Accordion() {
return (
<>
<Paneltitle="Ingredients">
Milk, tea bags, and a cinnamon stick.
<h2>Almaty, Kazakhstan</h2>
<Paneltitle="About">
With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
</Panel>
<Paneltitle="Recipe">
Heat the milk and put tea bags into the pan.
Add the cinnamon stick.
<Paneltitle="Etymology">
The name comes from <spanlang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <ilang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
</Panel>
</>
);
@ -73,13 +77,19 @@ Notice how pressing one panel's button does not affect the other panel--they are
**But now let's say you want to change it so that only one panel is expanded at any given time.** With that design, expanding the second panel should collapse the first one. How would you do that?
To coordinate these two panels, you need to "lift their state up" to a parent component in three steps
To coordinate these two panels, you need to "lift their state up" to a parent component in three steps:
1. **Remove** state from the child components.
2. **Pass** hardcoded data from the common parent.
3. **Add** state to the common parent and pass it down together with the event handlers.
This will allow the `Accordion` component to coordinate both `Panel`s and only expand one at a time.
<imgalt="On the left are two components each owning their own state values. On the right, they are the children of a parent component that owns both their state values."src="/images/docs/sketches/s_lifting-state-up.png"/>
### Step 1: Remove state from the child components
You will give control of the `Panel`'s `isActive` to a parent component of both `Panel`s. Then you'll pass the state back to the children as a prop. Start by **removing this line** from the `Panel` component:
You will give control of the `Panel`'s `isActive` to its parent component. This means that the parent component will pass `isActive` to `Panel` as a prop instead. Start by **removing this line** from the `Panel` component:
```js
const [isActive, setIsActive] = useState(false);
@ -111,12 +121,12 @@ import { useState } from 'react';
export default function Accordion() {
return (
<>
<Paneltitle="Ingredients"isActive={true}>
Milk, tea bags, and a cinnamon stick.
<h2>Almaty, Kazakhstan</h2>
<Paneltitle="About"isActive={true}>
With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
</Panel>
<Paneltitle="Recipe"isActive={true}>
Heat the milk and put tea bags into the pan.
Add the cinnamon stick.
<Paneltitle="Etymology"isActive={true}>
The name comes from <spanlang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <ilang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
</Panel>
</>
);
@ -152,7 +162,11 @@ Try editing the hardcoded `isActive` values in the `Accordion` component and see
### Step 3: Add state to the common parent
Lifting state up often changes the nature of what you're storing as state. In this case, only one panel should be active at a time. This means that the `Accordion` common parent component needs to keep track of *which* panel is the active one. Instead of a `boolean` value, it could use an number as the index of the active `Panel` for the state variable:
Lifting state up often changes the nature of what you're storing as state.
<imgalt="The parent component passes the state setting function to both child components."src="/images/docs/sketches/s_passing-functions-down.png"/>
In this case, only one panel should be active at a time. This means that the `Accordion` common parent component needs to keep track of *which* panel is the active one. Instead of a `boolean` value, it could use an number as the index of the active `Panel` for the state variable:
@ -163,18 +177,20 @@ When the `activeIndex` is `0`, the first panel is active, and when it's `1`, it'
Clicking the "Show" button in either `Panel` needs to change the active index in `Accordion`. A `Panel` can't set the `activeIndex` state directly because it's defined inside the `Accordion`. The `Accordion` component needs to *explicitly allow* the `Panel` component to change its state by [passing an event handler down as a prop](/learn/responding-to-events#passing-event-handlers-as-props):
```js
<Panel
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}
>
...
</Panel>
<Panel
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
...
</Panel>
<>
<Panel
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}
>
...
</Panel>
<Panel
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
...
</Panel>
</>
```
The `<button>` inside the `Panel` will now use the `onShow` prop as its click event handler:
@ -188,20 +204,20 @@ export default function Accordion() {
With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city.
</Panel>
<Panel
title="Recipe"
title="Etymology"
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
Heat the milk and put tea bags into the pan.
Add the cinnamon stick.
The name comes from <spanlang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <ilang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple.
</Panel>
</>
);
@ -246,7 +262,7 @@ It is common to call a component with some local state "uncontrolled". For examp
In contrast, you might say a component is "controlled" when the important information in it is driven by props rather than its own local state. This lets the parent component fully specify its behavior. The final `Panel` component with the `isActive` prop is controlled by the `Accordion` component.
Uncontrolled components are often less cumbersome to use from their parents because they require less configuration. But they're less flexible when you want to coordinate them together. Controlled components are maximally flexible, but they require the parent components to fully configure them with props.
Uncontrolled components are easier to use within their parents because they require less configuration. But they're less flexible when you want to coordinate them together. Controlled components are maximally flexible, but they require the parent components to fully configure them with props.
In practice, "controlled" and "uncontrolled" aren't strict technical terms--each component usually has some mix of both local state and props. However, this is a useful way to talk about how components are designed and what capabilities they offer.
@ -254,16 +270,16 @@ When writing a component, consider which information in it should be controlled
</DeepDive>
<imgalt="The parent component passes the state setting function to both child components."src="/images/docs/sketches/s_passing-functions-down.png"/>
## A single source of truth for each state
In a React application, many components will have their own state. Some state may "live" close to the leaf components like inputs. Other state may "live" closer to the top of the app. For example, even a router is usually implemented by storing the current route in the React state, and passing it down by props!
In a React application, many components will have their own state. Some state may "live" close to the leaf components (components at the bottom of the tree) like inputs. Other state may "live" closer to the top of the app. For example, even client-side routing libraries are usually implemented by storing the current route in the React state, and passing it down by props!
**For each unique piece of state, you will choose the component that "owns" it**. This principle is also known as having a "single source of truth." It doesn't mean that all state lives in one place--but that for _each_ piece of state, there is a _specific_ component that holds that information. Instead of duplicating shared state between components, you will *lift it up* to their common shared parent, and *pass it down* to the children that need it.
**For each unique piece of state, you will choose the component that "owns" it**. This principle is also known as having a ["single source of truth."](https://en.wikipedia.org/wiki/Single_source_of_truth) It doesn't mean that all state lives in one place--but that for _each_ piece of state, there is a _specific_ component that holds that piece of information. Instead of duplicating shared state between components, you will *lift it up* to their common shared parent, and *pass it down* to the children that need it.
Your app will change as you work on it. It is common that you will move state down or back up while you're still figuring out where each piece of the state "lives". This is all part of the process!
To see what this feels like in practice with a few more components, read [Thinking in React](/learn/thinking-in-react).
<Recap>
* When you want to coordinate two components, move their state to their common parent.
@ -273,13 +289,17 @@ Your app will change as you work on it. It is common that you will move state do
</Recap>
<Challenges>
### Synced inputs
These two inputs are independent. Make them stay in sync: editing one input should update the other input with the same text, and vice versa. You'll need to lift their state up into the parent component.
These two inputs are independent. Make them stay in sync: editing one input should update the other input with the same text, and vice versa.
<Hint>
You'll need to lift their state up into the parent component.
</Hint>
<Sandpack>
@ -476,7 +496,7 @@ export const foods = [{
<Solution>
Lift the `query` state up into the `FilterableList` component. Call `filterItems(foods, query)` to get the filtered list and pass it down to the `List`. Now changing the query input reflects in the list:
Lift the `query` state up into the `FilterableList` component. Call `filterItems(foods, query)` to get the filtered list and pass it down to the `List`. Now changing the query input is reflected in the list: