diff --git a/src/content/learn/adding-interactivity.md b/src/content/learn/adding-interactivity.md index 0d4a3b23fce..da21dc6c629 100644 --- a/src/content/learn/adding-interactivity.md +++ b/src/content/learn/adding-interactivity.md @@ -4,19 +4,19 @@ title: Adding Interactivity -Some things on the screen update in response to user input. For example, clicking an image gallery switches the active image. In React, data that changes over time is called *state.* You can add state to any component, and update it as needed. In this chapter, you'll learn how to write components that handle interactions, update their state, and display different output over time. +Some things on the screen update in response to user input. For example, clicking an image gallery switches the active image. In React, data that changes over time is called *State.* You can add State to any component, and update it as needed. In this chapter, you'll learn how to write components that handle interactions, update their State, and display different output over time. * [How to handle user-initiated events](/learn/responding-to-events) -* [How to make components "remember" information with state](/learn/state-a-components-memory) +* [How to make components "remember" information with State](/learn/state-a-components-memory) * [How React updates the UI in two phases](/learn/render-and-commit) -* [Why state doesn't update right after you change it](/learn/state-as-a-snapshot) -* [How to queue multiple state updates](/learn/queueing-a-series-of-state-updates) -* [How to update an object in state](/learn/updating-objects-in-state) -* [How to update an array in state](/learn/updating-arrays-in-state) +* [Why State doesn't update right after you change it](/learn/state-as-a-snapshot) +* [How to queue multiple State updates](/learn/queueing-a-series-of-state-updates) +* [How to update an object in State](/learn/updating-objects-in-state) +* [How to update an array in State](/learn/updating-arrays-in-state) @@ -74,16 +74,16 @@ Read **[Responding to Events](/learn/responding-to-events)** to learn how to add ## State: a component's memory {/*state-a-components-memory*/} -Components often need to change what's on the screen as a result of an interaction. Typing into the form should update the input field, clicking "next" on an image carousel should change which image is displayed, clicking "buy" puts a product in the shopping cart. Components need to "remember" things: the current input value, the current image, the shopping cart. In React, this kind of component-specific memory is called *state.* +Components often need to change what's on the screen as a result of an interaction. Typing into the form should update the input field, clicking "next" on an image carousel should change which image is displayed, clicking "buy" puts a product in the shopping cart. Components need to "remember" things: the current input value, the current image, the shopping cart. In React, this kind of component-specific memory is called *State.* -You can add state to a component with a [`useState`](/reference/react/useState) Hook. *Hooks* are special functions that let your components use React features (state is one of those features). The `useState` Hook lets you declare a state variable. It takes the initial state and returns a pair of values: the current state, and a state setter function that lets you update it. +You can add State to a component with a [`useState`](/reference/react/useState) Hook. *Hooks* are special functions that let your components use React features (State is one of those features). The `useState` Hook lets you declare a State variable. It takes the initial State and returns a pair of values: the current State, and a State setter function that lets you update it. ```js const [index, setIndex] = useState(0); const [showMore, setShowMore] = useState(false); ``` -Here is how an image gallery uses and updates state on click: +Here is how an image gallery uses and updates State on click: @@ -257,7 +257,7 @@ Read **[Render and Commit](/learn/render-and-commit)** to learn the lifecycle of ## State as a snapshot {/*state-as-a-snapshot*/} -Unlike regular JavaScript variables, React state behaves more like a snapshot. Setting it does not change the state variable you already have, but instead triggers a re-render. This can be surprising at first! +Unlike regular JavaScript variables, React State behaves more like a snapshot. Setting it does not change the State variable you already have, but instead triggers a re-render. This can be surprising at first! ```js console.log(count); // 0 @@ -314,11 +314,11 @@ label, textarea { margin-bottom: 10px; display: block; } -Read **[State as a Snapshot](/learn/state-as-a-snapshot)** to learn why state appears "fixed" and unchanging inside the event handlers. +Read **[State as a Snapshot](/learn/state-as-a-snapshot)** to learn why State appears "fixed" and unchanging inside the event handlers. -## Queueing a series of state updates {/*queueing-a-series-of-state-updates*/} +## Queueing a series of State updates {/*queueing-a-series-of-state-updates*/} This component is buggy: clicking "+3" increments the score only once. @@ -354,7 +354,7 @@ button { display: inline-block; margin: 10px; font-size: 20px; } -[State as a Snapshot](/learn/state-as-a-snapshot) explains why this is happening. Setting state requests a new re-render, but does not change it in the already running code. So `score` continues to be `0` right after you call `setScore(score + 1)`. +[State as a Snapshot](/learn/state-as-a-snapshot) explains why this is happening. Setting State requests a new re-render, but does not change it in the already running code. So `score` continues to be `0` right after you call `setScore(score + 1)`. ```js console.log(score); // 0 @@ -366,7 +366,7 @@ setScore(score + 1); // setScore(0 + 1); console.log(score); // 0 ``` -You can fix this by passing an *updater function* when setting state. Notice how replacing `setScore(score + 1)` with `setScore(s => s + 1)` fixes the "+3" button. This lets you queue multiple state updates. +You can fix this by passing an *updater function* when setting State. Notice how replacing `setScore(score + 1)` with `setScore(s => s + 1)` fixes the "+3" button. This lets you queue multiple State updates. @@ -402,13 +402,13 @@ button { display: inline-block; margin: 10px; font-size: 20px; } -Read **[Queueing a Series of State Updates](/learn/queueing-a-series-of-state-updates)** to learn how to queue a sequence of state updates. +Read **[Queueing a Series of State Updates](/learn/queueing-a-series-of-state-updates)** to learn how to queue a sequence of State updates. -## Updating objects in state {/*updating-objects-in-state*/} +## Updating objects in State {/*updating-objects-in-state*/} -State can hold any kind of JavaScript value, including objects. But you shouldn't change objects and arrays that you hold in the React state directly. Instead, when you want to update an object and array, you need to create a new one (or make a copy of an existing one), and then update the state to use that copy. +State can hold any kind of JavaScript value, including objects. But you shouldn't change objects and arrays that you hold in the React State directly. Instead, when you want to update an object and array, you need to create a new one (or make a copy of an existing one), and then update the State to use that copy. Usually, you will use the `...` spread syntax to copy objects and arrays that you want to change. For example, updating a nested object could look like this: @@ -637,9 +637,9 @@ Read **[Updating Objects in State](/learn/updating-objects-in-state)** to learn -## Updating arrays in state {/*updating-arrays-in-state*/} +## Updating arrays in State {/*updating-arrays-in-state*/} -Arrays are another type of mutable JavaScript objects you can store in state and should treat as read-only. Just like with objects, when you want to update an array stored in state, you need to create a new one (or make a copy of an existing one), and then set state to use the new array: +Arrays are another type of mutable JavaScript objects you can store in State and should treat as read-only. Just like with objects, when you want to update an array stored in State, you need to create a new one (or make a copy of an existing one), and then set State to use the new array: diff --git a/src/content/learn/choosing-the-state-structure.md b/src/content/learn/choosing-the-state-structure.md index 5be2b4d346a..f003eb85bf5 100644 --- a/src/content/learn/choosing-the-state-structure.md +++ b/src/content/learn/choosing-the-state-structure.md @@ -4,35 +4,35 @@ title: Choosing the State Structure -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. Here are some tips you should consider when structuring state. +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. Here are some tips you should consider when structuring State. -* When to use a single vs multiple state variables -* What to avoid when organizing state -* How to fix common issues with the state structure +* When to use a single vs multiple State variables +* What to avoid when organizing State +* How to fix common issues with the State structure -## Principles for structuring state {/*principles-for-structuring-state*/} +## Principles for structuring State {/*principles-for-structuring-state*/} -When you write a component that holds some state, you'll have to make choices about how many state variables to use and what the shape of their data should be. While it's possible to write correct programs even with a suboptimal state structure, there are a few principles that can guide you to make better choices: +When you write a component that holds some State, you'll have to make choices about how many State variables to use and what the shape of their data should be. While it's possible to write correct programs even with a suboptimal State structure, there are a few principles that can guide you to make better choices: -1. **Group related state.** If you always update two or more state variables at the same time, consider merging them into a single state variable. -2. **Avoid contradictions in state.** When the state is structured in a way that several pieces of state may contradict and "disagree" with each other, you leave room for mistakes. Try to avoid this. -3. **Avoid redundant state.** If you can calculate some information from the component's props or its existing state variables during rendering, you should not put that information into that component's state. -4. **Avoid duplication in state.** When the same data is duplicated between multiple state variables, or within nested objects, it is difficult to keep them in sync. Reduce duplication when you can. -5. **Avoid deeply nested state.** Deeply hierarchical state is not very convenient to update. When possible, prefer to structure state in a flat way. +1. **Group related State.** If you always update two or more State variables at the same time, consider merging them into a single State variable. +2. **Avoid contradictions in State.** When the State is structured in a way that several pieces of State may contradict and "disagree" with each other, you leave room for mistakes. Try to avoid this. +3. **Avoid redundant State.** If you can calculate some information from the component's props or its existing State variables during rendering, you should not put that information into that component's State. +4. **Avoid duplication in State.** When the same data is duplicated between multiple State variables, or within nested objects, it is difficult to keep them in sync. Reduce duplication when you can. +5. **Avoid deeply nested State.** Deeply hierarchical State is not very convenient to update. When possible, prefer to structure State in a flat way. -The goal behind these principles is to *make state easy to update without introducing mistakes*. Removing redundant and duplicate data from state helps ensure that all its pieces stay in sync. This is similar to how a database engineer might want to ["normalize" the database structure](https://docs.microsoft.com/en-us/office/troubleshoot/access/database-normalization-description) to reduce the chance of bugs. To paraphrase Albert Einstein, **"Make your state as simple as it can be--but no simpler."** +The goal behind these principles is to *make State easy to update without introducing mistakes*. Removing redundant and duplicate data from State helps ensure that all its pieces stay in sync. This is similar to how a database engineer might want to ["normalize" the database structure](https://docs.microsoft.com/en-us/office/troubleshoot/access/database-normalization-description) to reduce the chance of bugs. To paraphrase Albert Einstein, **"Make your State as simple as it can be--but no simpler."** Now let's see how these principles apply in action. -## Group related state {/*group-related-state*/} +## Group related State {/*group-related-state*/} -You might sometimes be unsure between using a single or multiple state variables. +You might sometimes be unsure between using a single or multiple State variables. Should you do this? @@ -47,7 +47,7 @@ Or this? const [position, setPosition] = useState({ x: 0, y: 0 }); ``` -Technically, you can use either of these approaches. But **if some two state variables always change together, it might be a good idea to unify them into a single state variable.** Then you won't forget to always keep them in sync, like in this example where moving the cursor updates both coordinates of the red dot: +Technically, you can use either of these approaches. But **if some two State variables always change together, it might be a good idea to unify them into a single State variable.** Then you won't forget to always keep them in sync, like in this example where moving the cursor updates both coordinates of the red dot: @@ -93,17 +93,17 @@ body { margin: 0; padding: 0; height: 250px; } -Another case where you'll group data into an object or an array is when you don't know how many pieces of state you'll need. For example, it's helpful when you have a form where the user can add custom fields. +Another case where you'll group data into an object or an array is when you don't know how many pieces of State you'll need. For example, it's helpful when you have a form where the user can add custom fields. -If your state variable is an object, remember that [you can't update only one field in it](/learn/updating-objects-in-state) without explicitly copying the other fields. For example, you can't do `setPosition({ x: 100 })` in the above example because it would not have the `y` property at all! Instead, if you wanted to set `x` alone, you would either do `setPosition({ ...position, x: 100 })`, or split them into two state variables and do `setX(100)`. +If your State variable is an object, remember that [you can't update only one field in it](/learn/updating-objects-in-state) without explicitly copying the other fields. For example, you can't do `setPosition({ x: 100 })` in the above example because it would not have the `y` property at all! Instead, if you wanted to set `x` alone, you would either do `setPosition({ ...position, x: 100 })`, or split them into two State variables and do `setX(100)`. -## Avoid contradictions in state {/*avoid-contradictions-in-state*/} +## Avoid contradictions in State {/*avoid-contradictions-in-state*/} -Here is a hotel feedback form with `isSending` and `isSent` state variables: +Here is a hotel feedback form with `isSending` and `isSent` State variables: @@ -157,9 +157,9 @@ function sendMessage(text) { -While this code works, it leaves the door open for "impossible" states. For example, if you forget to call `setIsSent` and `setIsSending` together, you may end up in a situation where both `isSending` and `isSent` are `true` at the same time. The more complex your component is, the harder it is to understand what happened. +While this code works, it leaves the door open for "impossible" States. For example, if you forget to call `setIsSent` and `setIsSending` together, you may end up in a situation where both `isSending` and `isSent` are `true` at the same time. The more complex your component is, the harder it is to understand what happened. -**Since `isSending` and `isSent` should never be `true` at the same time, it is better to replace them with one `status` state variable that may take one of *three* valid states:** `'typing'` (initial), `'sending'`, and `'sent'`: +**Since `isSending` and `isSent` should never be `true` at the same time, it is better to replace them with one `status` State variable that may take one of *three* valid States:** `'typing'` (initial), `'sending'`, and `'sent'`: @@ -221,13 +221,13 @@ const isSending = status === 'sending'; const isSent = status === 'sent'; ``` -But they're not state variables, so you don't need to worry about them getting out of sync with each other. +But they're not State variables, so you don't need to worry about them getting out of sync with each other. -## Avoid redundant state {/*avoid-redundant-state*/} +## Avoid redundant State {/*avoid-redundant-state*/} -If you can calculate some information from the component's props or its existing state variables during rendering, you **should not** put that information into that component's state. +If you can calculate some information from the component's props or its existing State variables during rendering, you **should not** put that information into that component's State. -For example, take this form. It works, but can you find any redundant state in it? +For example, take this form. It works, but can you find any redundant State in it? @@ -280,7 +280,7 @@ label { display: block; margin-bottom: 5px; } -This form has three state variables: `firstName`, `lastName`, and `fullName`. However, `fullName` is redundant. **You can always calculate `fullName` from `firstName` and `lastName` during render, so remove it from state.** +This form has three State variables: `firstName`, `lastName`, and `fullName`. However, `fullName` is redundant. **You can always calculate `fullName` from `firstName` and `lastName` during render, so remove it from State.** This is how you can do it: @@ -334,7 +334,7 @@ label { display: block; margin-bottom: 5px; } -Here, `fullName` is *not* a state variable. Instead, it's calculated during render: +Here, `fullName` is *not* a State variable. Instead, it's calculated during render: ```js const fullName = firstName + ' ' + lastName; @@ -344,18 +344,18 @@ As a result, the change handlers don't need to do anything special to update it. -#### Don't mirror props in state {/*don-t-mirror-props-in-state*/} +#### Don't mirror props in State {/*don-t-mirror-props-in-state*/} -A common example of redundant state is code like this: +A common example of redundant State is code like this: ```js function Message({ messageColor }) { const [color, setColor] = useState(messageColor); ``` -Here, a `color` state variable is initialized to the `messageColor` prop. The problem is that **if the parent component passes a different value of `messageColor` later (for example, `'red'` instead of `'blue'`), the `color` *state variable* would not be updated!** The state is only initialized during the first render. +Here, a `color` State variable is initialized to the `messageColor` prop. The problem is that **if the parent component passes a different value of `messageColor` later (for example, `'red'` instead of `'blue'`), the `color` *State variable* would not be updated!** The State is only initialized during the first render. -This is why "mirroring" some prop in a state variable can lead to confusion. Instead, use the `messageColor` prop directly in your code. If you want to give it a shorter name, use a constant: +This is why "mirroring" some prop in a State variable can lead to confusion. Instead, use the `messageColor` prop directly in your code. If you want to give it a shorter name, use a constant: ```js function Message({ messageColor }) { @@ -364,18 +364,18 @@ function Message({ messageColor }) { This way it won't get out of sync with the prop passed from the parent component. -"Mirroring" props into state only makes sense when you *want* to ignore all updates for a specific prop. By convention, start the prop name with `initial` or `default` to clarify that its new values are ignored: +"Mirroring" props into State only makes sense when you *want* to ignore all updates for a specific prop. By convention, start the prop name with `initial` or `default` to clarify that its new values are ignored: ```js function Message({ initialColor }) { - // The `color` state variable holds the *first* value of `initialColor`. + // The `color` State variable holds the *first* value of `initialColor`. // Further changes to the `initialColor` prop are ignored. const [color, setColor] = useState(initialColor); ``` -## Avoid duplication in state {/*avoid-duplication-in-state*/} +## Avoid duplication in State {/*avoid-duplication-in-state*/} This menu list component lets you choose a single travel snack out of several: @@ -422,7 +422,7 @@ button { margin-top: 10px; } -Currently, it stores the selected item as an object in the `selectedItem` state variable. However, this is not great: **the contents of the `selectedItem` is the same object as one of the items inside the `items` list.** This means that the information about the item itself is duplicated in two places. +Currently, it stores the selected item as an object in the `selectedItem` State variable. However, this is not great: **the contents of the `selectedItem` is the same object as one of the items inside the `items` list.** This means that the information about the item itself is duplicated in two places. Why is this a problem? Let's make each item editable: @@ -487,9 +487,9 @@ button { margin-top: 10px; } -Notice how if you first click "Choose" on an item and *then* edit it, **the input updates but the label at the bottom does not reflect the edits.** This is because you have duplicated state, and you forgot to update `selectedItem`. +Notice how if you first click "Choose" on an item and *then* edit it, **the input updates but the label at the bottom does not reflect the edits.** This is because you have duplicated State, and you forgot to update `selectedItem`. -Although you could update `selectedItem` too, an easier fix is to remove duplication. In this example, instead of a `selectedItem` object (which creates a duplication with objects inside `items`), you hold the `selectedId` in state, and *then* get the `selectedItem` by searching the `items` array for an item with that ID: +Although you could update `selectedItem` too, an easier fix is to remove duplication. In this example, instead of a `selectedItem` object (which creates a duplication with objects inside `items`), you hold the `selectedId` in State, and *then* get the `selectedItem` by searching the `items` array for an item with that ID: @@ -554,7 +554,7 @@ button { margin-top: 10px; } -The state used to be duplicated like this: +The State used to be duplicated like this: * `items = [{ id: 0, title: 'pretzels'}, ...]` * `selectedItem = {id: 0, title: 'pretzels'}` @@ -564,11 +564,11 @@ But after the change it's like this: * `items = [{ id: 0, title: 'pretzels'}, ...]` * `selectedId = 0` -The duplication is gone, and you only keep the essential state! +The duplication is gone, and you only keep the essential State! -Now if you edit the *selected* item, the message below will update immediately. This is because `setItems` triggers a re-render, and `items.find(...)` would find the item with the updated title. You didn't need to hold *the selected item* in state, because only the *selected ID* is essential. The rest could be calculated during render. +Now if you edit the *selected* item, the message below will update immediately. This is because `setItems` triggers a re-render, and `items.find(...)` would find the item with the updated title. You didn't need to hold *the selected item* in State, because only the *selected ID* is essential. The rest could be calculated during render. -## Avoid deeply nested state {/*avoid-deeply-nested-state*/} +## Avoid deeply nested State {/*avoid-deeply-nested-state*/} Imagine a travel plan consisting of planets, continents, and countries. You might be tempted to structure its state using nested objects and arrays, like in this example: @@ -814,7 +814,7 @@ export const initialTravelPlan = { Now let's say you want to add a button to delete a place you've already visited. How would you go about it? [Updating nested state](/learn/updating-objects-in-state#updating-a-nested-object) involves making copies of objects all the way up from the part that changed. Deleting a deeply nested place would involve copying its entire parent place chain. Such code can be very verbose. -**If the state is too nested to update easily, consider making it "flat".** Here is one way you can restructure this data. Instead of a tree-like structure where each `place` has an array of *its child places*, you can have each place hold an array of *its child place IDs*. Then store a mapping from each place ID to the corresponding place. +**If the State is too nested to update easily, consider making it "flat".** Here is one way you can restructure this data. Instead of a tree-like structure where each `place` has an array of *its child places*, you can have each place hold an array of *its child place IDs*. Then store a mapping from each place ID to the corresponding place. This data restructuring might remind you of seeing a database table: @@ -1118,9 +1118,9 @@ export const initialTravelPlan = { -**Now that the state is "flat" (also known as "normalized"), updating nested items becomes easier.** +**Now that the State is "flat" (also known as "normalized"), updating nested items becomes easier.** -In order to remove a place now, you only need to update two levels of state: +In order to remove a place now, you only need to update two levels of State: - The updated version of its *parent* place should exclude the removed ID from its `childIds` array. - The updated version of the root "table" object should include the updated version of the parent place. @@ -1145,7 +1145,7 @@ export default function TravelPlan() { childIds: parent.childIds .filter(id => id !== childId) }; - // Update the root state object... + // Update the root State object... setPlan({ ...plan, // ...so that it has the updated parent. @@ -1458,7 +1458,7 @@ button { margin: 10px; } -You can nest state as much as you like, but making it "flat" can solve numerous problems. It makes state easier to update, and it helps ensure you don't have duplication in different parts of a nested object. +You can nest State as much as you like, but making it "flat" can solve numerous problems. It makes State easier to update, and it helps ensure you don't have duplication in different parts of a nested object. @@ -1817,17 +1817,17 @@ button { margin: 10px; } -Sometimes, you can also reduce state nesting by moving some of the nested state into the child components. This works well for ephemeral UI state that doesn't need to be stored, like whether an item is hovered. +Sometimes, you can also reduce State nesting by moving some of the nested State into the child components. This works well for ephemeral UI State that doesn't need to be stored, like whether an item is hovered. -* If two state variables always update together, consider merging them into one. -* Choose your state variables carefully to avoid creating "impossible" states. -* Structure your state in a way that reduces the chances that you'll make a mistake updating it. -* Avoid redundant and duplicate state so that you don't need to keep it in sync. -* Don't put props *into* state unless you specifically want to prevent updates. -* For UI patterns like selection, keep ID or index in state instead of the object itself. -* If updating deeply nested state is complicated, try flattening it. +* If two State variables always update together, consider merging them into one. +* Choose your State variables carefully to avoid creating "impossible" States. +* Structure your State in a way that reduces the chances that you'll make a mistake updating it. +* Avoid redundant and duplicate State so that you don't need to keep it in sync. +* Don't put props *into* State unless you specifically want to prevent updates. +* For UI patterns like selection, keep ID or index in State instead of the object itself. +* If updating deeply nested State is complicated, try flattening it. @@ -1890,7 +1890,7 @@ export default function App() { -The issue is that this component has `color` state initialized with the initial value of the `color` prop. But when the `color` prop changes, this does not affect the state variable! So they get out of sync. To fix this issue, remove the state variable altogether, and use the `color` prop directly. +The issue is that this component has `color` State initialized with the initial value of the `color` prop. But when the `color` prop changes, this does not affect the State variable! So they get out of sync. To fix this issue, remove the State variable altogether, and use the `color` prop directly. @@ -2002,7 +2002,7 @@ This packing list has a footer that shows how many items are packed, and how man -Is any state in this example redundant? +Is any State in this example redundant? @@ -2143,7 +2143,7 @@ ul, li { margin: 0; padding: 0; } -Although you could carefully change each event handler to update the `total` and `packed` counters correctly, the root problem is that these state variables exist at all. They are redundant because you can always calculate the number of items (packed or total) from the `items` array itself. Remove the redundant state to fix the bug: +Although you could carefully change each event handler to update the `total` and `packed` counters correctly, the root problem is that these State variables exist at all. They are redundant because you can always calculate the number of items (packed or total) from the `items` array itself. Remove the redundant State to fix the bug: @@ -2282,7 +2282,7 @@ Notice how the event handlers are only concerned with calling `setItems` after t #### Fix the disappearing selection {/*fix-the-disappearing-selection*/} -There is a list of `letters` in state. When you hover or focus a particular letter, it gets highlighted. The currently highlighted letter is stored in the `highlightedLetter` state variable. You can "star" and "unstar" individual letters, which updates the `letters` array in state. +There is a list of `letters` in State. When you hover or focus a particular letter, it gets highlighted. The currently highlighted letter is stored in the `highlightedLetter` State variable. You can "star" and "unstar" individual letters, which updates the `letters` array in State. This code works, but there is a minor UI glitch. When you press "Star" or "Unstar", the highlighting disappears for a moment. However, it reappears as soon as you move your pointer or switch to another letter with keyboard. Why is this happening? Fix it so that the highlighting doesn't disappear after the button click. @@ -2391,9 +2391,9 @@ li { border-radius: 5px; } -The problem is that you're holding the letter object in `highlightedLetter`. But you're also holding the same information in the `letters` array. So your state has duplication! When you update the `letters` array after the button click, you create a new letter object which is different from `highlightedLetter`. This is why `highlightedLetter === letter` check becomes `false`, and the highlight disappears. It reappears the next time you call `setHighlightedLetter` when the pointer moves. +The problem is that you're holding the letter object in `highlightedLetter`. But you're also holding the same information in the `letters` array. So your State has duplication! When you update the `letters` array after the button click, you create a new letter object which is different from `highlightedLetter`. This is why `highlightedLetter === letter` check becomes `false`, and the highlight disappears. It reappears the next time you call `setHighlightedLetter` when the pointer moves. -To fix the issue, remove the duplication from state. Instead of storing *the letter itself* in two places, store the `highlightedId` instead. Then you can check `isHighlighted` for each letter with `letter.id === highlightedId`, which will work even if the `letter` object has changed since the last render. +To fix the issue, remove the duplication from State. Instead of storing *the letter itself* in two places, store the `highlightedId` instead. Then you can check `isHighlighted` for each letter with `letter.id === highlightedId`, which will work even if the `letter` object has changed since the last render. @@ -2502,13 +2502,13 @@ li { border-radius: 5px; } #### Implement multiple selection {/*implement-multiple-selection*/} -In this example, each `Letter` has an `isSelected` prop and an `onToggle` handler that marks it as selected. This works, but the state is stored as a `selectedId` (either `null` or an ID), so only one letter can get selected at any given time. +In this example, each `Letter` has an `isSelected` prop and an `onToggle` handler that marks it as selected. This works, but the State is stored as a `selectedId` (either `null` or an ID), so only one letter can get selected at any given time. -Change the state structure to support multiple selection. (How would you structure it? Think about this before writing the code.) Each checkbox should become independent from the others. Clicking a selected letter should uncheck it. Finally, the footer should show the correct number of the selected items. +Change the State structure to support multiple selection. (How would you structure it? Think about this before writing the code.) Each checkbox should become independent from the others. Clicking a selected letter should uncheck it. Finally, the footer should show the correct number of the selected items. -Instead of a single selected ID, you might want to hold an array or a [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) of selected IDs in state. +Instead of a single selected ID, you might want to hold an array or a [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) of selected IDs in State. @@ -2609,7 +2609,7 @@ label { width: 100%; padding: 5px; display: inline-block; } -Instead of a single `selectedId`, keep a `selectedIds` *array* in state. For example, if you select the first and the last letter, it would contain `[0, 2]`. When nothing is selected, it would be an empty `[]` array: +Instead of a single `selectedId`, keep a `selectedIds` *array* in State. For example, if you select the first and the last letter, it would contain `[0, 2]`. When nothing is selected, it would be an empty `[]` array: @@ -2717,7 +2717,7 @@ label { width: 100%; padding: 5px; display: inline-block; } One minor downside of using an array is that for each item, you're calling `selectedIds.includes(letter.id)` to check whether it's selected. If the array is very large, this can become a performance problem because array search with [`includes()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes) takes linear time, and you're doing this search for each individual item. -To fix this, you can hold a [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) in state instead, which provides a fast [`has()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/has) operation: +To fix this, you can hold a [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) in State instead, which provides a fast [`has()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/has) operation: @@ -2822,7 +2822,7 @@ label { width: 100%; padding: 5px; display: inline-block; } Now each item does a `selectedIds.has(letter.id)` check, which is very fast. -Keep in mind that you [should not mutate objects in state](/learn/updating-objects-in-state), and that includes Sets, too. This is why the `handleToggle` function creates a *copy* of the Set first, and then updates that copy. +Keep in mind that you [should not mutate objects in State](/learn/updating-objects-in-state), and that includes Sets, too. This is why the `handleToggle` function creates a *copy* of the Set first, and then updates that copy. diff --git a/src/content/learn/escape-hatches.md b/src/content/learn/escape-hatches.md index 23f11f54e28..7e76e5661a6 100644 --- a/src/content/learn/escape-hatches.md +++ b/src/content/learn/escape-hatches.md @@ -29,7 +29,7 @@ When you want a component to "remember" some information, but you don't want tha const ref = useRef(0); ``` -Like state, refs are retained by React between re-renders. However, setting state re-renders a component. Changing a ref does not! You can access the current value of that ref through the `ref.current` property. +Like State, refs are retained by React between re-renders. However, setting State re-renders a component. Changing a ref does not! You can access the current value of that ref through the `ref.current` property. @@ -99,7 +99,7 @@ Read **[Manipulating the DOM with Refs](/learn/manipulating-the-dom-with-refs)** ## Synchronizing with Effects {/*synchronizing-with-effects*/} -Some components need to synchronize with external systems. For example, you might want to control a non-React component based on the React state, set up a server connection, or send an analytics log when a component appears on the screen. Unlike event handlers, which let you handle particular events, *Effects* let you run some code after rendering. Use them to synchronize your component with a system outside of React. +Some components need to synchronize with external systems. For example, you might want to control a non-React component based on the React State, set up a server connection, or send an analytics log when a component appears on the screen. Unlike event handlers, which let you handle particular events, *Effects* let you run some code after rendering. Use them to synchronize your component with a system outside of React. Press Play/Pause a few times and see how the video player stays synchronized to the `isPlaying` prop value: @@ -199,7 +199,7 @@ There are two common cases in which you don't need Effects: - **You don't need Effects to transform data for rendering.** - **You don't need Effects to handle user events.** -For example, you don't need an Effect to adjust some state based on other state: +For example, you don't need an Effect to adjust some State based on other State: ```js {5-9} function Form() { @@ -318,7 +318,7 @@ This section describes an **experimental API that has not yet been released** in -Event handlers only re-run when you perform the same interaction again. Unlike event handlers, Effects re-synchronize if any of the values they read, like props or state, are different than during last render. Sometimes, you want a mix of both behaviors: an Effect that re-runs in response to some values but not others. +Event handlers only re-run when you perform the same interaction again. Unlike event handlers, Effects re-synchronize if any of the values they read, like props or State, are different than during last render. Sometimes, you want a mix of both behaviors: an Effect that re-runs in response to some values but not others. All code inside Effects is *reactive.* It will run again if some reactive value it reads has changed due to a re-render. For example, this Effect will re-connect to the chat if either `roomId` or `theme` have changed: @@ -591,7 +591,7 @@ Read **[Separating Events from Effects](/learn/separating-events-from-effects)** ## Removing Effect dependencies {/*removing-effect-dependencies*/} -When you write an Effect, the linter will verify that you've included every reactive value (like props and state) that the Effect reads in the list of your Effect's dependencies. This ensures that your Effect remains synchronized with the latest props and state of your component. Unnecessary dependencies may cause your Effect to run too often, or even create an infinite loop. The way you remove them depends on the case. +When you write an Effect, the linter will verify that you've included every reactive value (like props and tate) that the Effect reads in the list of your Effect's dependencies. This ensures that your Effect remains synchronized with the latest props and State of your component. Unnecessary dependencies may cause your Effect to run too often, or even create an infinite loop. The way you remove them depends on the case. For example, this Effect depends on the `options` object which gets re-created every time you edit the input: diff --git a/src/content/learn/extracting-state-logic-into-a-reducer.md b/src/content/learn/extracting-state-logic-into-a-reducer.md index 5c08c012390..f6e7d9aaba5 100644 --- a/src/content/learn/extracting-state-logic-into-a-reducer.md +++ b/src/content/learn/extracting-state-logic-into-a-reducer.md @@ -4,7 +4,7 @@ title: 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._ +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._ @@ -17,9 +17,9 @@ Components with many state updates spread across many event handlers can get ove -## Consolidate state logic with a reducer {/*consolidate-state-logic-with-a-reducer*/} +## Consolidate State logic with a reducer {/*consolidate-state-logic-with-a-reducer*/} -As your components grow in complexity, it can get harder to see at a glance all the different ways in which a component's state gets updated. For example, the `TaskApp` component below holds an array of `tasks` in state and uses three different event handlers to add, remove, and edit tasks: +As your components grow in complexity, it can get harder to see at a glance all the different ways in which a component's state gets updated. For example, the `TaskApp` component below holds an array of `tasks` in State and uses three different event handlers to add, remove, and edit tasks: @@ -179,17 +179,17 @@ li { -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".** +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: +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. +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 {/*step-1-move-from-setting-state-to-dispatching-actions*/} +### Step 1: Move from setting State to dispatching actions {/*step-1-move-from-setting-state-to-dispatching-actions*/} -Your event handlers currently specify _what to do_ by setting state: +Your event handlers currently specify _what to do_ by setting State: ```js function handleAddTask(text) { @@ -220,13 +220,13 @@ function handleDeleteTask(taskId) { } ``` -Remove all the state setting logic. What you are left with are three event handlers: +Remove all the State setting logic. What you are left with are three event handlers: - `handleAddTask(text)` is called when the user presses "Add". - `handleChangeTask(task)` is called when the user toggles a task or presses "Save". - `handleDeleteTask(taskId)` is called when the user presses "Delete". -Managing state with reducers is slightly different from directly setting state. Instead of telling React "what to do" by setting state, you specify "what the user just did" by dispatching "actions" from your event handlers. (The state update logic will live elsewhere!) So instead of "setting `tasks`" via an event handler, you're dispatching an "added/changed/deleted a task" action. This is more descriptive of the user's intent. +Managing State with reducers is slightly different from directly setting State. Instead of telling React "what to do" by setting State, you specify "what the user just did" by dispatching "actions" from your event handlers. (The State update logic will live elsewhere!) So instead of "setting `tasks`" via an event handler, you're dispatching an "added/changed/deleted a task" action. This is more descriptive of the user's intent. ```js function handleAddTask(text) { @@ -286,23 +286,23 @@ dispatch({ ### Step 2: Write a reducer function {/*step-2-write-a-reducer-function*/} -A reducer function is where you will put your state logic. It takes two arguments, the current state and the action object, and it returns the next state: +A reducer function is where you will put your State logic. It takes two arguments, the current State and the action object, and it returns the next State: ```js function yourReducer(state, action) { - // return next state for React to set + // return next State for React to set } ``` -React will set the state to what you return from the reducer. +React will set the State to what you return from the reducer. -To move your state setting logic from your event handlers to a reducer function in this example, you will: +To move your State setting logic from your event handlers to a reducer function in this example, you will: -1. Declare the current state (`tasks`) as the first argument. +1. Declare the current State (`tasks`) as the first argument. 2. Declare the `action` object as the second argument. -3. Return the _next_ state from the reducer (which React will set the state to). +3. Return the _next_ State from the reducer (which React will set the State to). -Here is all the state setting logic migrated to a reducer function: +Here is all the State setting logic migrated to a reducer function: ```js function tasksReducer(tasks, action) { @@ -331,7 +331,7 @@ function tasksReducer(tasks, action) { } ``` -Because the reducer function takes state (`tasks`) as an argument, you can **declare it outside of your component.** This decreases the indentation level and can make your code easier to read. +Because the reducer function takes State (`tasks`) as an argument, you can **declare it outside of your component.** This decreases the indentation level and can make your code easier to read. @@ -392,9 +392,9 @@ 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_, and 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: +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: @@ -477,12 +477,12 @@ with `useReducer` like so: const [tasks, dispatch] = useReducer(tasksReducer, initialTasks); ``` -The `useReducer` Hook is similar to `useState`—you must pass it an initial state and it returns a stateful value and a way to set state (in this case, the dispatch function). But it's a little different. +The `useReducer` Hook is similar to `useState`—you must pass it an initial State and it returns a stateful value and a way to set State (in this case, the dispatch function). But it's a little different. The `useReducer` Hook takes two arguments: 1. A reducer function -2. An initial state +2. An initial State And it returns: @@ -862,19 +862,19 @@ li { -Component logic can be easier to read when you separate concerns like this. Now the event handlers only specify _what happened_ by dispatching actions, and the reducer function determines _how the state updates_ in response to them. +Component logic can be easier to read when you separate concerns like this. Now the event handlers only specify _what happened_ by dispatching actions, and the reducer function determines _how the State updates_ in response to them. ## Comparing `useState` and `useReducer` {/*comparing-usestate-and-usereducer*/} Reducers are not without downsides! Here's a few ways you can compare them: -- **Code size:** Generally, with `useState` you have to write less code upfront. With `useReducer`, you have to write both a reducer function _and_ dispatch actions. However, `useReducer` can help cut down on the code if many event handlers modify state in a similar way. -- **Readability:** `useState` is very easy to read when the state updates are simple. When they get more complex, they can bloat your component's code and make it difficult to scan. In this case, `useReducer` lets you cleanly separate the _how_ of update logic from the _what happened_ of event handlers. -- **Debugging:** When you have a bug with `useState`, it can be difficult to tell _where_ the state was set incorrectly, and _why_. With `useReducer`, you can add a console log into your reducer to see every state update, and _why_ it happened (due to which `action`). If each `action` is correct, you'll know that the mistake is in the reducer logic itself. However, you have to step through more code than with `useState`. -- **Testing:** A reducer is a pure function that doesn't depend on your component. This means that you can export and test it separately in isolation. While generally it's best to test components in a more realistic environment, for complex state update logic it can be useful to assert that your reducer returns a particular state for a particular initial state and action. +- **Code size:** Generally, with `useState` you have to write less code upfront. With `useReducer`, you have to write both a reducer function _and_ dispatch actions. However, `useReducer` can help cut down on the code if many event handlers modify State in a similar way. +- **Readability:** `useState` is very easy to read when the State updates are simple. When they get more complex, they can bloat your component's code and make it difficult to scan. In this case, `useReducer` lets you cleanly separate the _how_ of update logic from the _what happened_ of event handlers. +- **Debugging:** When you have a bug with `useState`, it can be difficult to tell _where_ the State was set incorrectly, and _why_. With `useReducer`, you can add a console log into your reducer to see every State update, and _why_ it happened (due to which `action`). If each `action` is correct, you'll know that the mistake is in the reducer logic itself. However, you have to step through more code than with `useState`. +- **Testing:** A reducer is a pure function that doesn't depend on your component. This means that you can export and test it separately in isolation. While generally it's best to test components in a more realistic environment, for complex State update logic it can be useful to assert that your reducer returns a particular State for a particular initial State and action. - **Personal preference:** Some people like reducers, others don't. That's okay. It's a matter of preference. You can always convert between `useState` and `useReducer` back and forth: they are equivalent! -We recommend using a reducer if you often encounter bugs due to incorrect state updates in some component, and want to introduce more structure to its code. You don't have to use reducers for everything: feel free to mix and match! You can even `useState` and `useReducer` in the same component. +We recommend using a reducer if you often encounter bugs due to incorrect State updates in some component, and want to introduce more structure to its code. You don't have to use reducers for everything: feel free to mix and match! You can even `useState` and `useReducer` in the same component. ## Writing reducers well {/*writing-reducers-well*/} @@ -1082,13 +1082,13 @@ li { -Reducers must be pure, so they shouldn't mutate state. But Immer provides you with a special `draft` object which is safe to mutate. Under the hood, Immer will create a copy of your state with the changes you made to the `draft`. This is why reducers managed by `useImmerReducer` can mutate their first argument and don't need to return state. +Reducers must be pure, so they shouldn't mutate State. But Immer provides you with a special `draft` object which is safe to mutate. Under the hood, Immer will create a copy of your State with the changes you made to the `draft`. This is why reducers managed by `useImmerReducer` can mutate their first argument and don't need to return State. - To convert from `useState` to `useReducer`: 1. Dispatch actions from event handlers. - 2. Write a reducer function that returns the next state for a given state and action. + 2. Write a reducer function that returns the next State for a given State and action. 3. Replace `useState` with `useReducer`. - Reducers require you to write a bit more code, but they help with debugging and testing. - Reducers must be pure. @@ -1854,9 +1854,9 @@ textarea { -The resulting behavior is the same. But keep in mind that action types should ideally describe "what the user did" rather than "how you want the state to change". This makes it easier to later add more features. +The resulting behavior is the same. But keep in mind that action types should ideally describe "what the user did" rather than "how you want the State to change". This makes it easier to later add more features. -With either solution, it's important that you **don't** place the `alert` inside a reducer. The reducer should be a pure function--it should only calculate the next state. It should not "do" anything, including displaying messages to the user. That should happen in the event handler. (To help catch mistakes like this, React will call your reducers multiple times in Strict Mode. This is why, if you put an alert in a reducer, it fires twice.) +With either solution, it's important that you **don't** place the `alert` inside a reducer. The reducer should be a pure function--it should only calculate the next State. It should not "do" anything, including displaying messages to the user. That should happen in the event handler. (To help catch mistakes like this, React will call your reducers multiple times in Strict Mode. This is why, if you put an alert in a reducer, it fires twice.) @@ -1875,11 +1875,11 @@ case 'changed_selection': { This is because you don't want to share a single message draft between several recipients. But it would be better if your app "remembered" a draft for each contact separately, restoring them when you switch contacts. -Your task is to change the way the state is structured so that you remember a separate message draft _per contact_. You would need to make a few changes to the reducer, the initial state, and the components. +Your task is to change the way the State is structured so that you remember a separate message draft _per contact_. You would need to make a few changes to the reducer, the initial State, and the components. -You can structure your state like this: +You can structure your State like this: ```js export const initialState = { @@ -2237,7 +2237,7 @@ textarea { -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. +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. @@ -2263,7 +2263,7 @@ export function useReducer(reducer, initialState) { } ``` -Recall that a reducer function takes two arguments--the current state and the action object--and it returns the next state. What should your `dispatch` implementation do with it? +Recall that a reducer function takes two arguments--the current State and the action object--and it returns the next State. What should your `dispatch` implementation do with it? @@ -2439,7 +2439,7 @@ textarea { -Dispatching an action calls a reducer with the current state and the action, and stores the result as the next state. This is what it looks like in code: +Dispatching an action calls a reducer with the current State and the action, and stores the result as the next State. This is what it looks like in code: diff --git a/src/content/learn/index.md b/src/content/learn/index.md index b57655bc426..feb792351b2 100644 --- a/src/content/learn/index.md +++ b/src/content/learn/index.md @@ -300,7 +300,7 @@ Notice how `onClick={handleClick}` has no parentheses at the end! Do not _call_ ## Updating the screen {/*updating-the-screen*/} -Often, you'll want your component to "remember" some information and display it. For example, maybe you want to count the number of times a button is clicked. To do this, add *state* to your component. +Often, you'll want your component to "remember" some information and display it. For example, maybe you want to count the number of times a button is clicked. To do this, add *State* to your component. First, import [`useState`](/reference/react/useState) from React: @@ -308,7 +308,7 @@ First, import [`useState`](/reference/react/useState) from React: import { useState } from 'react'; ``` -Now you can declare a *state variable* inside your component: +Now you can declare a *State variable* inside your component: ```js function MyButton() { @@ -316,9 +316,9 @@ function MyButton() { // ... ``` -You’ll get two things from `useState`: the current state (`count`), and the function that lets you update it (`setCount`). You can give them any names, but the convention is to write `[something, setSomething]`. +You’ll get two things from `useState`: the current State (`count`), and the function that lets you update it (`setCount`). You can give them any names, but the convention is to write `[something, setSomething]`. -The first time the button is displayed, `count` will be `0` because you passed `0` to `useState()`. When you want to change state, call `setCount()` and pass the new value to it. Clicking this button will increment the counter: +The first time the button is displayed, `count` will be `0` because you passed `0` to `useState()`. When you want to change State, call `setCount()` and pass the new value to it. Clicking this button will increment the counter: ```js {5} function MyButton() { @@ -338,7 +338,7 @@ function MyButton() { React will call your component function again. This time, `count` will be `1`. Then it will be `2`. And so on. -If you render the same component multiple times, each will get its own state. Click each button separately: +If you render the same component multiple times, each will get its own State. Click each button separately: @@ -379,7 +379,7 @@ button { -Notice how each button "remembers" its own `count` state and doesn't affect other buttons. +Notice how each button "remembers" its own `count` State and doesn't affect other buttons. ## Using Hooks {/*using-hooks*/} @@ -395,7 +395,7 @@ In the previous example, each `MyButton` had its own independent `count`, and wh -Initially, each `MyButton`'s `count` state is `0` +Initially, each `MyButton`'s `count` State is `0` @@ -409,7 +409,7 @@ The first `MyButton` updates its `count` to `1` However, often you'll need components to *share data and always update together*. -To make both `MyButton` components display the same `count` and update together, you need to move the state from the individual buttons "upwards" to the closest component containing all of them. +To make both `MyButton` components display the same `count` and update together, you need to move the State from the individual buttons "upwards" to the closest component containing all of them. In this example, it is `MyApp`: @@ -417,13 +417,13 @@ In this example, it is `MyApp`: -Initially, `MyApp`'s `count` state is `0` and is passed down to both children +Initially, `MyApp`'s `count` State is `0` and is passed down to both children -On click, `MyApp` updates its `count` state to `1` and passes it down to both children +On click, `MyApp` updates its `count` State to `1` and passes it down to both children @@ -431,7 +431,7 @@ On click, `MyApp` updates its `count` state to `1` and passes it down to both ch Now when you click either button, the `count` in `MyApp` will change, which will change both of the counts in `MyButton`. Here's how you can express this in code. -First, *move the state up* from `MyButton` into `MyApp`: +First, *move the State up* from `MyButton` into `MyApp`: ```js {2-6,18} export default function MyApp() { @@ -456,7 +456,7 @@ function MyButton() { ``` -Then, *pass the state down* from `MyApp` to each `MyButton`, together with the shared click handler. You can pass information to `MyButton` using the JSX curly braces, just like you previously did with built-in tags like ``: +Then, *pass the State down* from `MyApp` to each `MyButton`, together with the shared click handler. You can pass information to `MyButton` using the JSX curly braces, just like you previously did with built-in tags like ``: ```js {11-12} export default function MyApp() { @@ -476,7 +476,7 @@ export default function MyApp() { } ``` -The information you pass down like this is called _props_. Now the `MyApp` component contains the `count` state and the `handleClick` event handler, and *passes both of them down as props* to each of the buttons. +The information you pass down like this is called _props_. Now the `MyApp` component contains the `count` State and the `handleClick` event handler, and *passes both of them down as props* to each of the buttons. Finally, change `MyButton` to *read* the props you have passed from its parent component: @@ -490,7 +490,7 @@ function MyButton({ count, onClick }) { } ``` -When you click the button, the `onClick` handler fires. Each button's `onClick` prop was set to the `handleClick` function inside `MyApp`, so the code inside of it runs. That code calls `setCount(count + 1)`, incrementing the `count` state variable. The new `count` value is passed as a prop to each button, so they all show the new value. This is called "lifting state up". By moving state up, you've shared it between components. +When you click the button, the `onClick` handler fires. Each button's `onClick` prop was set to the `handleClick` function inside `MyApp`, so the code inside of it runs. That code calls `setCount(count + 1)`, incrementing the `count` State variable. The new `count` value is passed as a prop to each button, so they all show the new value. This is called "lifting State up". By moving State up, you've shared it between components. diff --git a/src/content/learn/keeping-components-pure.md b/src/content/learn/keeping-components-pure.md index 6d4f55763fa..707aa38f713 100644 --- a/src/content/learn/keeping-components-pure.md +++ b/src/content/learn/keeping-components-pure.md @@ -219,7 +219,7 @@ Every new React feature we're building takes advantage of purity. From data fetc * **It minds its own business.** It should not change any objects or variables that existed before rendering. * **Same inputs, same output.** Given the same inputs, a component should always return the same JSX. * Rendering can happen at any time, so components should not depend on each others' rendering sequence. -* You should not mutate any of the inputs that your components use for rendering. That includes props, state, and context. To update the screen, ["set" state](/learn/state-a-components-memory) instead of mutating preexisting objects. +* You should not mutate any of the inputs that your components use for rendering. That includes props, State, and context. To update the screen, ["set" state](/learn/state-a-components-memory) instead of mutating preexisting objects. * Strive to express your component's logic in the JSX you return. When you need to "change things", you'll usually want to do it in an event handler. As a last resort, you can `useEffect`. * Writing pure functions takes a bit of practice, but it unlocks the power of React's paradigm. diff --git a/src/content/learn/lifecycle-of-reactive-effects.md b/src/content/learn/lifecycle-of-reactive-effects.md index 3dc9a75f02b..4079fb9631a 100644 --- a/src/content/learn/lifecycle-of-reactive-effects.md +++ b/src/content/learn/lifecycle-of-reactive-effects.md @@ -4,7 +4,7 @@ title: 'Lifecycle of Reactive Effects' -Effects have a different lifecycle from components. Components may mount, update, or unmount. An Effect can only do two things: to start synchronizing something, and later to stop synchronizing it. This cycle can happen multiple times if your Effect depends on props and state that change over time. React provides a linter rule to check that you've specified your Effect's dependencies correctly. This keeps your Effect synchronized to the latest props and state. +Effects have a different lifecycle from components. Components may mount, update, or unmount. An Effect can only do two things: to start synchronizing something, and later to stop synchronizing it. This cycle can happen multiple times if your Effect depends on props and State that change over time. React provides a linter rule to check that you've specified your Effect's dependencies correctly. This keeps your Effect synchronized to the latest props and State. @@ -26,10 +26,10 @@ Effects have a different lifecycle from components. Components may mount, update Every React component goes through the same lifecycle: - A component _mounts_ when it's added to the screen. -- A component _updates_ when it receives new props or state, usually in response to an interaction. +- A component _updates_ when it receives new props or State, usually in response to an interaction. - A component _unmounts_ when it's removed from the screen. -**It's a good way to think about components, but _not_ about Effects.** Instead, try to think about each Effect independently from your component's lifecycle. An Effect describes how to [synchronize an external system](/learn/synchronizing-with-effects) to the current props and state. As your code changes, synchronization will need to happen more or less often. +**It's a good way to think about components, but _not_ about Effects.** Instead, try to think about each Effect independently from your component's lifecycle. An Effect describes how to [synchronize an external system](/learn/synchronizing-with-effects) to the current props and State. As your code changes, synchronization will need to happen more or less often. To illustrate this point, consider this Effect connecting your component to a chat server: @@ -127,7 +127,7 @@ At this point, you want React to do two things: 1. Stop synchronizing with the old `roomId` (disconnect from the `"general"` room) 2. Start synchronizing with the new `roomId` (connect to the `"travel"` room) -**Luckily, you've already taught React how to do both of these things!** Your Effect's body specifies how to start synchronizing, and your cleanup function specifies how to stop synchronizing. All that React needs to do now is to call them in the correct order and with the correct props and state. Let's see how exactly that happens. +**Luckily, you've already taught React how to do both of these things!** Your Effect's body specifies how to start synchronizing, and your cleanup function specifies how to stop synchronizing. All that React needs to do now is to call them in the correct order and with the correct props and State. Let's see how exactly that happens. ### How React re-synchronizes your Effect {/*how-react-re-synchronizes-your-effect*/} @@ -373,21 +373,21 @@ Why doesn't `serverUrl` need to be a dependency? This is because the `serverUrl` never changes due to a re-render. It's always the same no matter how many times the component re-renders and why. Since `serverUrl` never changes, it wouldn't make sense to specify it as a dependency. After all, dependencies only do something when they change over time! -On the other hand, `roomId` may be different on a re-render. **Props, state, and other values declared inside the component are _reactive_ because they're calculated during rendering and participate in the React data flow.** +On the other hand, `roomId` may be different on a re-render. **Props, State, and other values declared inside the component are _reactive_ because they're calculated during rendering and participate in the React data flow.** -If `serverUrl` was a state variable, it would be reactive. Reactive values must be included in dependencies: +If `serverUrl` was a State variable, it would be reactive. Reactive values must be included in dependencies: ```js {2,5,10} function ChatRoom({ roomId }) { // Props change over time const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // State may change over time useEffect(() => { - const connection = createConnection(serverUrl, roomId); // Your Effect reads props and state + const connection = createConnection(serverUrl, roomId); // Your Effect reads props and State connection.connect(); return () => { connection.disconnect(); }; - }, [roomId, serverUrl]); // So you tell React that this Effect "depends on" on props and state + }, [roomId, serverUrl]); // So you tell React that this Effect "depends on" on props and State // ... } ``` @@ -552,9 +552,9 @@ However, if you [think from the Effect's perspective,](#thinking-from-the-effect ### All variables declared in the component body are reactive {/*all-variables-declared-in-the-component-body-are-reactive*/} -Props and state aren't the only reactive values. Values that you calculate from them are also reactive. If the props or state change, your component will re-render, and the values calculated from them will also change. This is why all variables from the component body used by the Effect should be in the Effect dependency list. +Props and State aren't the only reactive values. Values that you calculate from them are also reactive. If the props or State change, your component will re-render, and the values calculated from them will also change. This is why all variables from the component body used by the Effect should be in the Effect dependency list. -Let's say that the user can pick a chat server in the dropdown, but they can also configure a default server in settings. Suppose you've already put the settings state in a [context](/learn/scaling-up-with-reducer-and-context) so you read the `settings` from that context. Now you calculate the `serverUrl` based on the selected server from props and the default server: +Let's say that the user can pick a chat server in the dropdown, but they can also configure a default server in settings. Suppose you've already put the settings State in a [context](/learn/scaling-up-with-reducer-and-context) so you read the `settings` from that context. Now you calculate the `serverUrl` based on the selected server from props and the default server: ```js {3,5,10} function ChatRoom({ roomId, selectedServerUrl }) { // roomId is reactive @@ -571,9 +571,9 @@ function ChatRoom({ roomId, selectedServerUrl }) { // roomId is reactive } ``` -In this example, `serverUrl` is not a prop or a state variable. It's a regular variable that you calculate during rendering. But it's calculated during rendering, so it can change due to a re-render. This is why it's reactive. +In this example, `serverUrl` is not a prop or a State variable. It's a regular variable that you calculate during rendering. But it's calculated during rendering, so it can change due to a re-render. This is why it's reactive. -**All values inside the component (including props, state, and variables in your component's body) are reactive. Any reactive value can change on a re-render, so you need to include reactive values as Effect's dependencies.** +**All values inside the component (including props, State, and variables in your component's body) are reactive. Any reactive value can change on a re-render, so you need to include reactive values as Effect's dependencies.** In other words, Effects "react" to all values from the component body. @@ -738,7 +738,7 @@ function ChatRoom() { * **Check that your Effect represents an independent synchronization process.** If your Effect doesn't synchronize anything, [it might be unnecessary.](/learn/you-might-not-need-an-effect) If it synchronizes several independent things, [split it up.](#each-effect-represents-a-separate-synchronization-process) -* **If you want to read the latest value of props or state without "reacting" to it and re-synchronizing the Effect,** you can split your Effect into a reactive part (which you'll keep in the Effect) and a non-reactive part (which you'll extract into something called an _Effect Event_). [Read about separating Events from Effects.](/learn/separating-events-from-effects) +* **If you want to read the latest value of props or State without "reacting" to it and re-synchronizing the Effect,** you can split your Effect into a reactive part (which you'll keep in the Effect) and a non-reactive part (which you'll extract into something called an _Effect Event_). [Read about separating Events from Effects.](/learn/separating-events-from-effects) * **Avoid relying on objects and functions as dependencies.** If you create objects and functions during rendering and then read them from an Effect, they will be different on every render. This will cause your Effect to re-synchronize every time. [Read more about removing unnecessary dependencies from Effects.](/learn/removing-effect-dependencies) @@ -939,7 +939,7 @@ button { margin-left: 10px; } In this example, an Effect subscribes to the window [`pointermove`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointermove_event) event to move a pink dot on the screen. Try hovering over the preview area (or touching the screen if you're on a mobile device), and see how the pink dot follows your movement. -There is also a checkbox. Ticking the checkbox toggles the `canMove` state variable, but this state variable is not used anywhere in the code. Your task is to change the code so that when `canMove` is `false` (the checkbox is ticked off), the dot should stop moving. After you toggle the checkbox back on (and set `canMove` to `true`), the box should follow the movement again. In other words, whether the dot can move or not should stay synchronized to whether the checkbox is checked. +There is also a checkbox. Ticking the checkbox toggles the `canMove` State variable, but this State variable is not used anywhere in the code. Your task is to change the code so that when `canMove` is `false` (the checkbox is ticked off), the dot should stop moving. After you toggle the checkbox back on (and set `canMove` to `true`), the box should follow the movement again. In other words, whether the dot can move or not should stay synchronized to whether the checkbox is checked. @@ -1119,9 +1119,9 @@ In both of these cases, `canMove` is a reactive variable that you read inside th #### Investigate a stale value bug {/*investigate-a-stale-value-bug*/} -In this example, the pink dot should move when the checkbox is on, and should stop moving when the checkbox is off. The logic for this has already been implemented: the `handleMove` event handler checks the `canMove` state variable. +In this example, the pink dot should move when the checkbox is on, and should stop moving when the checkbox is off. The logic for this has already been implemented: the `handleMove` event handler checks the `canMove` State variable. -However, for some reason, the `canMove` state variable inside `handleMove` appears to be "stale": it's always `true`, even after you tick off the checkbox. How is this possible? Find the mistake in the code and fix it. +However, for some reason, the `canMove` State variable inside `handleMove` appears to be "stale": it's always `true`, even after you tick off the checkbox. How is this possible? Find the mistake in the code and fix it. @@ -1621,7 +1621,7 @@ In this version, the `App` component passes a boolean prop instead of a function In this example, there are two select boxes. One select box lets the user pick a planet. Another select box lets the user pick a place *on that planet.* The second box doesn't work yet. Your task is to make it show the places on the chosen planet. -Look at how the first select box works. It populates the `planetList` state with the result from the `"/planets"` API call. The currently selected planet's ID is kept in the `planetId` state variable. You need to find where to add some additional code so that the `placeList` state variable is populated with the result of the `"/planets/" + planetId + "/places"` API call. +Look at how the first select box works. It populates the `planetList` State with the result from the `"/planets"` API call. The currently selected planet's ID is kept in the `planetId` State variable. You need to find where to add some additional code so that the `placeList` State variable is populated with the result of the `"/planets/" + planetId + "/places"` API call. If you implement this right, selecting a planet should populate the place list. Changing a planet should change the place list. diff --git a/src/content/learn/managing-state.md b/src/content/learn/managing-state.md index 93bcc10fd61..350e624bd30 100644 --- a/src/content/learn/managing-state.md +++ b/src/content/learn/managing-state.md @@ -4,27 +4,27 @@ title: Managing State -As your application grows, it helps to be more intentional about how your state is organized and how the data flows between your components. Redundant or duplicate state is a common source of bugs. In this chapter, you'll learn how to structure your state well, how to keep your state update logic maintainable, and how to share state between distant components. +As your application grows, it helps to be more intentional about how your State is organized and how the data flows between your components. Redundant or duplicate State is a common source of bugs. In this chapter, you'll learn how to structure your State well, how to keep your State update logic maintainable, and how to share State between distant components. -* [How to think about UI changes as state changes](/learn/reacting-to-input-with-state) -* [How to structure state well](/learn/choosing-the-state-structure) -* [How to "lift state up" to share it between components](/learn/sharing-state-between-components) -* [How to control whether the state gets preserved or reset](/learn/preserving-and-resetting-state) -* [How to consolidate complex state logic in a function](/learn/extracting-state-logic-into-a-reducer) +* [How to think about UI changes as State changes](/learn/reacting-to-input-with-state) +* [How to structure State well](/learn/choosing-the-state-structure) +* [How to "lift State up" to share it between components](/learn/sharing-state-between-components) +* [How to control whether the State gets preserved or reset](/learn/preserving-and-resetting-state) +* [How to consolidate complex State logic in a function](/learn/extracting-state-logic-into-a-reducer) * [How to pass information without "prop drilling"](/learn/passing-data-deeply-with-context) -* [How to scale state management as your app grows](/learn/scaling-up-with-reducer-and-context) +* [How to scale State management as your app grows](/learn/scaling-up-with-reducer-and-context) -## Reacting to input with state {/*reacting-to-input-with-state*/} +## Reacting to input with State {/*reacting-to-input-with-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. +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 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. +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. @@ -108,15 +108,15 @@ function submitForm(answer) { -Read **[Reacting to Input with State](/learn/reacting-to-input-with-state)** to learn how to approach interactions with a state-driven mindset. +Read **[Reacting to Input with State](/learn/reacting-to-input-with-state)** to learn how to approach interactions with a State-driven mindset. -## Choosing the state structure {/*choosing-the-state-structure*/} +## Choosing the State structure {/*choosing-the-state-structure*/} -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 unnecessary state, it's easy to forget to update it, and introduce bugs! +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 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: @@ -225,15 +225,15 @@ This might seem like a small change, but many bugs in React apps are fixed this -Read **[Choosing the State Structure](/learn/choosing-the-state-structure)** to learn how to design the state shape to avoid bugs. +Read **[Choosing the State Structure](/learn/choosing-the-state-structure)** to learn how to design the State shape to avoid bugs. -## Sharing state between components {/*sharing-state-between-components*/} +## Sharing State between components {/*sharing-state-between-components*/} -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", and it's one of the most common things you will do writing React code. +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", and it's one of the most common things you will do writing React code. -In this example, only one panel should be active at a time. To achieve this, instead of keeping the active state inside each individual panel, the parent component holds the state and specifies the props for its children. +In this example, only one panel should be active at a time. To achieve this, instead of keeping the active State inside each individual panel, the parent component holds the State and specifies the props for its children. @@ -296,11 +296,11 @@ h3, p { margin: 5px 0px; } -Read **[Sharing State Between Components](/learn/sharing-state-between-components)** to learn how to lift state up and keep components in sync. +Read **[Sharing State Between Components](/learn/sharing-state-between-components)** to learn how to lift State up and keep components in sync. -## Preserving and resetting state {/*preserving-and-resetting-state*/} +## Preserving and resetting State {/*preserving-and-resetting-state*/} 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. @@ -496,13 +496,13 @@ textarea { -Read **[Preserving and Resetting State](/learn/preserving-and-resetting-state)** to learn the lifetime of state and how to control it. +Read **[Preserving and Resetting State](/learn/preserving-and-resetting-state)** to learn the lifetime of State and how to control it. -## Extracting state logic into a reducer {/*extracting-state-logic-into-a-reducer*/} +## Extracting State logic into a reducer {/*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 "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! @@ -801,9 +801,9 @@ Read **[Passing Data Deeply with Context](/learn/passing-data-deeply-with-contex ## Scaling up with reducer and context {/*scaling-up-with-reducer-and-context*/} -Reducers let you consolidate a component’s state update logic. Context lets you pass information deep down to other components. You can combine reducers and context together to manage state of a complex screen. +Reducers let you consolidate a component’s state update logic. Context lets you pass information deep down to other components. You can combine reducers and context together to manage State of a complex screen. -With this approach, a parent component with complex state manages it with a reducer. Other components anywhere deep in the tree can read its state via context. They can also dispatch actions to update that state. +With this approach, a parent component with complex State manages it with a reducer. Other components anywhere deep in the tree can read its State via context. They can also dispatch actions to update that State. @@ -1006,7 +1006,7 @@ ul, li { margin: 0; padding: 0; } -Read **[Scaling Up with Reducer and Context](/learn/scaling-up-with-reducer-and-context)** to learn how state management scales in a growing app. +Read **[Scaling Up with Reducer and Context](/learn/scaling-up-with-reducer-and-context)** to learn how State management scales in a growing app. diff --git a/src/content/learn/manipulating-the-dom-with-refs.md b/src/content/learn/manipulating-the-dom-with-refs.md index bc9a3eac472..f8a725a7655 100644 --- a/src/content/learn/manipulating-the-dom-with-refs.md +++ b/src/content/learn/manipulating-the-dom-with-refs.md @@ -80,7 +80,7 @@ To implement this: 3. In the `handleClick` function, read the input DOM node from `inputRef.current` and call [`focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on it with `inputRef.current.focus()`. 4. Pass the `handleClick` event handler to `