Browse Source

[Beta] Reorder API sections (#4328)

* Reorder API pages

* Tweaks
main
dan 3 years ago
committed by GitHub
parent
commit
0209e1b5be
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 20
      beta/src/components/MDX/MDXComponents.tsx
  2. 11
      beta/src/pages/apis/index.md
  3. 8
      beta/src/pages/apis/reactdom.md
  4. 147
      beta/src/pages/apis/render.md
  5. 490
      beta/src/pages/apis/usestate.md

20
beta/src/components/MDX/MDXComponents.tsx

@ -3,6 +3,7 @@
*/
import * as React from 'react';
import cn from 'classnames';
import {APIAnatomy, AnatomyStep} from './APIAnatomy';
import CodeBlock from './CodeBlock';
@ -26,6 +27,24 @@ import {Challenges, Hint, Solution} from './Challenges';
import {IconNavArrow} from '../Icon/IconNavArrow';
import ButtonLink from 'components/ButtonLink';
function CodeStep({children, step}: {children: any; step: number}) {
return (
<span
data-step={step}
className={cn(
'code-step bg-opacity-10 relative rounded-md p-1 ml-2 pl-3 before:content-[attr(data-step)] before:block before:w-4 before:h-4 before:absolute before:top-1 before:-left-2 before:rounded-full before:text-white before:text-center before:text-xs before:leading-4',
{
'bg-blue-40 before:bg-blue-40': step === 1,
'bg-yellow-40 before:bg-yellow-40': step === 2,
'bg-green-40 before:bg-green-40': step === 3,
'bg-purple-40 before:bg-purple-40': step === 4,
}
)}>
{children}
</span>
);
}
const P = (p: JSX.IntrinsicElements['p']) => (
<p className="whitespace-pre-wrap my-4" {...p} />
);
@ -373,4 +392,5 @@ export const MDXComponents = {
Challenges,
Hint,
Solution,
CodeStep,
};

11
beta/src/pages/apis/index.md

@ -34,9 +34,16 @@ If you use React on the web, you'll also need the same version of [ReactDOM](/ap
## Exports {/*exports*/}
<YouWillLearnCard title="useState" path="/reference/usestate">
<YouWillLearnCard title="useState" path="/apis/usestate">
Declares a state variable.
```js
function MyComponent() {
const [age, setAge] = useState(42);
// ...
```
A React Hook that lets a component "remember" some information (called state).
</YouWillLearnCard>

8
beta/src/pages/apis/reactdom.md

@ -38,9 +38,13 @@ ReactDOM supports all popular browsers, including Internet Explorer 9 and above.
## Exports {/*exports*/}
<YouWillLearnCard title="render" path="/reference/render">
<YouWillLearnCard title="render" path="/apis/render">
Renders a piece of JSX ("React element") into a browser DOM container node.
Displays a React component inside a browser DOM node.
```js
render(<App />, document.getElementById('root'));
```
</YouWillLearnCard>

147
beta/src/pages/apis/render.md

@ -1,104 +1,40 @@
---
title: render()
title: render
---
<Intro>
`render` renders a piece of [JSX](/learn/writing-markup-with-jsx) ("React node") into a browser DOM node.
</Intro>
```js
render(reactNode, domNode, callback?)
```
## On this page {/*on-this-page*/}
</Intro>
- [Reference](#reference)
- [`render(reactNode, domNode)`](#render)
- [`render(reactNode, domNode, callback)`](#render-callback)
- [Usage](#usage)
- [Rendering the root component](#rendering-the-root-component)
- [Rendering multiple roots](#rendering-multiple-roots)
- [Updating the rendered tree](#updating-the-rendered-tree)
## Reference {/*reference*/}
### `render(reactNode, domNode)` {/*render*/}
Call `render` to display a React component inside a browser DOM element.
```js
const domNode = document.getElementById('root');
render(<App />, domNode);
```
React will display `<App />` in the `domNode`, and take over managing the DOM inside it.
An app fully built with React will usually only have one `render` call with its root component. A page that uses "sprinkles" of React for parts of the page may have as many `render` calls as needed.
[See examples below.](#usage)
#### Parameters {/*parameters*/}
* `reactNode`: A *React node* that you want to display. This will usually be a piece of JSX like `<App />`, but you can also pass a React element constructed with [`createElement()`](/TODO), a string, a number, `null`, or `undefined`.
* `domNode`: A [DOM element](https://developer.mozilla.org/en-US/docs/Web/API/Element). React will display the `reactNode` you pass inside this DOM element. From this moment, React will manage the DOM inside the `domNode` and update it when your React tree changes.
#### Returns {/*returns*/}
`render` usually returns `null`. However, if the `reactNode` you pass is a *class component*, then it will return an instance of that component.
#### Caveats {/*caveats*/}
* The first time you call `render`, React will clear all the existing HTML content inside the `domNode` before rendering the React component into it. If your `domNode` contains HTML generated by React on the server or during the build, use [`hydrate()`](/TODO) instead, which attaches the event handlers to the existing HTML.
* If you call `render` on the same `domNode` more than once, React will update the DOM as necessary to reflect the latest JSX you passed. React will decide which parts of the DOM can be reused and which need to be recreated by ["matching it up"](/learn/preserving-and-resetting-state) with the previously rendered tree. Calling `render` on the same `domNode` again is similar to calling the [`set` function](/apis/usestate#setstate) on the root component: React avoids unnecessary DOM updates.
* If your app is fully built with React, you'll likely have only one `render` call in your app. (If you use a framework, it might do this call for you.) When you want to render a piece of JSX in a different part of the DOM tree that isn't a child of your component (for example, a modal or a tooltip), use [`createPortal`](TODO) instead of `render`.
---
### `render(reactNode, domNode, callback)` {/*render-callback*/}
Same as [`render(reactNode, domNode)`](#render), but with a `callback` that notifies you when your component has been placed into the DOM.
#### Parameters {/*render-callback-parameters*/}
* `reactNode`: [Same as above.](#parameters)
* `domNode`: [Same as above.](#parameters)
* `callback`: A function. If passed, React will call it after your component is placed into the DOM.
#### Returns {/*render-callback-returns*/}
[Same as above.](#returns)
- [Reference](#reference)
- [`render(reactNode, domNode, callback?)`](#render)
---
## Usage {/*usage*/}
### Rendering the root component {/*rendering-the-root-component*/}
To call `render`, you need a piece of JSX and a DOM container:
<APIAnatomy>
<AnatomyStep title="React node">
The UI you want to render.
Call `render` to display a <CodeStep step={1}>React component</CodeStep> inside a <CodeStep step={2}>browser DOM node</CodeStep>.
</AnatomyStep>
<AnatomyStep title="DOM node">
The DOM node you want to render your UI into. The container itself isn’t modified, only its children are.
</AnatomyStep>
```js [[1, 4, "<App />"], [2, 4, "document.getElementById('root')"]]
import {render} from 'react-dom';
import App from './App.js';
```js [[1, 2, "<App />"], [2, 2, "domNode"]]
const domNode = document.getElementById('root');
render(<App />, domNode);
```
render(<App />, document.getElementById('root'));
````
</APIAnatomy>
### Rendering the root component {/*rendering-the-root-component*/}
In apps fully built with React, you will do this once at the top level of your app--to render the "root" component.
In apps fully built with React, **you will usually only do this once at startup**--to render the "root" component.
<Sandpack>
@ -118,11 +54,13 @@ export default function App() {
</Sandpack>
Usually you shouldn't need to call `render` again or to call it in more places. From this point on, React will be managing the DOM of your application. If you want to update the UI, your components can do this by [using state](/apis/usestate).
---
### Rendering multiple roots {/*rendering-multiple-roots*/}
If you use ["sprinkles"](/learn/add-react-to-a-website) of React here and there, call `render` for each top-level piece of UI managed by React.
If your page [isn't fully built with React](/learn/add-react-to-a-website), call `render` for each top-level piece of UI managed by React.
<Sandpack>
@ -192,16 +130,19 @@ nav ul li { display: inline-block; margin-right: 20px; }
</Sandpack>
You can destroy the rendered trees with [`unmountComponentAtNode()`](TODO).
---
### Updating the rendered tree {/*updating-the-rendered-tree*/}
You can call `render` more than once on the same DOM node. As long as the component tree structure matches up with what was previously rendered, React will [preserve the state](/learn/preserving-and-resetting-state). Notice how you can type in the input:
You can call `render` more than once on the same DOM node. As long as the component tree structure matches up with what was previously rendered, React will [preserve the state](/learn/preserving-and-resetting-state). Notice how you can type in the input, which means that the updates from repeated `render` calls every second in this example are not destructive:
<Sandpack>
```js index.js active
import {render} from 'react-dom';
import './styles.css';
import App from './App.js';
let i = 0;
@ -227,4 +168,46 @@ export default function App({counter}) {
</Sandpack>
You can destroy the rendered tree with [`unmountComponentAtNode()`](TODO).
It is uncommon to call `render` multiple times. Usually, you'll [update state](/apis/usestate) inside one of the components instead.
---
## Reference {/*reference*/}
### `render(reactNode, domNode, callback?)` {/*render*/}
Call `render` to display a React component inside a browser DOM element.
```js
const domNode = document.getElementById('root');
render(<App />, domNode);
```
React will display `<App />` in the `domNode`, and take over managing the DOM inside it.
An app fully built with React will usually only have one `render` call with its root component. A page that uses "sprinkles" of React for parts of the page may have as many `render` calls as needed.
[See examples above.](#usage)
#### Parameters {/*parameters*/}
* `reactNode`: A *React node* that you want to display. This will usually be a piece of JSX like `<App />`, but you can also pass a React element constructed with [`createElement()`](/TODO), a string, a number, `null`, or `undefined`.
* `domNode`: A [DOM element](https://developer.mozilla.org/en-US/docs/Web/API/Element). React will display the `reactNode` you pass inside this DOM element. From this moment, React will manage the DOM inside the `domNode` and update it when your React tree changes.
* **optional** `callback`: A function. If passed, React will call it after your component is placed into the DOM.
#### Returns {/*returns*/}
`render` usually returns `null`. However, if the `reactNode` you pass is a *class component*, then it will return an instance of that component.
#### Caveats {/*caveats*/}
* The first time you call `render`, React will clear all the existing HTML content inside the `domNode` before rendering the React component into it. If your `domNode` contains HTML generated by React on the server or during the build, use [`hydrate()`](/TODO) instead, which attaches the event handlers to the existing HTML.
* If you call `render` on the same `domNode` more than once, React will update the DOM as necessary to reflect the latest JSX you passed. React will decide which parts of the DOM can be reused and which need to be recreated by ["matching it up"](/learn/preserving-and-resetting-state) with the previously rendered tree. Calling `render` on the same `domNode` again is similar to calling the [`set` function](/apis/usestate#setstate) on the root component: React avoids unnecessary DOM updates.
* If your app is fully built with React, you'll likely have only one `render` call in your app. (If you use a framework, it might do this call for you.) When you want to render a piece of JSX in a different part of the DOM tree that isn't a child of your component (for example, a modal or a tooltip), use [`createPortal`](TODO) instead of `render`.
---

490
beta/src/pages/apis/usestate.md

@ -6,13 +6,12 @@ title: useState
`useState` is a React Hook that lets you add a [state variable](/learn/state-a-components-memory) to your component.
</Intro>
```js
const [state, setState] = useState(initialState)
```
## On this page {/*on-this-page*/}
</Intro>
- [Reference](#reference)
- [`useState(initialState)`](#usestate)
- [`set` functions, like `setSomething(nextState)`](#setstate)
- [Usage](#usage)
- [Adding state to a component](#adding-state-to-a-component)
- [Updating state based on the previous state](#updating-state-based-on-the-previous-state)
@ -20,133 +19,64 @@ title: useState
- [Avoiding recreating the initial state](#avoiding-recreating-the-initial-state)
- [Resetting state with a key](#resetting-state-with-a-key)
- [Storing information from previous renders](#storing-information-from-previous-renders)
- [Reference](#reference)
- [`useState(initialState)`](#usestate)
- [`set` functions, like `setSomething(nextState)`](#setstate)
- [Troubleshooting](#troubleshooting)
- [I’ve updated the state, but logging gives me the old value](#ive-updated-the-state-but-logging-gives-me-the-old-value)
- [I've updated the state, but the screen doesn't update](#ive-updated-the-state-but-the-screen-doesnt-update)
- [I'm getting an error: "Too many re-renders"](#im-getting-an-error-too-many-re-renders)
- [My initializer or updater function runs twice](#my-initializer-or-updater-function-runs-twice)
- [I'm trying to set state to a function, but it gets called instead](#im-trying-to-set-state-to-a-function-but-it-gets-called-instead)
---
## Reference {/*reference*/}
## Usage {/*usage*/}
### `useState(initialState)` {/*usestate*/}
### Adding state to a component {/*adding-state-to-a-component*/}
Call `useState` at the top level of your component to declare a [state variable](/learn/state-a-components-memory).
Call `useState` at the top level of your component to declare one or more [state variables](/learn/state-a-components-memory).
```js
```js [[1, 4, "age"], [2, 4, "setAge"], [3, 4, "42"], [1, 5, "name"], [2, 5, "setName"], [3, 5, "'Taylor'"]]
import { useState } from 'react';
function MyComponent() {
const [age, setAge] = useState(28);
const [age, setAge] = useState(42);
const [name, setName] = useState('Taylor');
const [todos, setTodos] = useState(() => createTodos());
// ...
```
The convention is to name state variables like `[something, setSomething]` using [array destructuring](/learn/a-javascript-refresher#array-destructuring).
[See more examples below.](#examples-basic)
#### Parameters {/*parameters*/}
* `initialState`: The value you want the state to be initially. It can be a value of any type, but there is a special behavior for functions. This argument is ignored after the initial render.
* If you pass a function as `initialState`, it will be treated as an _initializer function_. It should be pure, should take no arguments, and should return a value of any type. React will call your initializer function when initializing the component, and store its return value as the initial state. [See an example below.](#avoiding-recreating-the-initial-state)
#### Returns {/*returns*/}
`useState` returns an array with exactly two values:
1. The current state. During the first render, it will match the `initialState` you have passed.
2. The [`set` function](#setstate) that lets you update the state to a different value and trigger a re-render.
#### Caveats {/*caveats*/}
* `useState` is a Hook, so you can only call it **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a new component and move the state into it.
* In Strict Mode, React will **call your initializer function twice** in order to [help you find accidental impurities](#my-initializer-or-updater-function-runs-twice). This is development-only behavior and does not affect production. If your initializer function is pure (as it should be), this should not affect the logic of your component. The result from one of the calls will be ignored.
---
### `set` functions, like `setSomething(nextState)` {/*setstate*/}
`useState` returns an array with exactly two items:
The `set` function returned by `useState` lets you update the state to a different value and trigger a re-render. You can pass the next state directly, or a function that calculates it from the previous state:
1. The <CodeStep step={1}>current state</CodeStep> of this state variable, initially set to the <CodeStep step={3}>initial state</CodeStep> you provided.
2. The <CodeStep step={2}>`set` function</CodeStep> that lets you change it to any other value in response to interaction.
```js
const [name, setName] = useState('Edward');
To update what’s on the screen, call the `set` function with some next state:
```js [[2, 2, "setName"]]
function handleClick() {
setName('Taylor');
setAge(a => a + 1);
// ...
setName('Robin');
}
```
#### Parameters {/*setstate-parameters*/}
* `nextState`: The value that you want the state to be. It can be a value of any type, but there is a special behavior for functions.
* If you pass a function as `nextState`, it will be treated as an _updater function_. It must be pure, should take the pending state as its only argument, and should return the next state. React will put your updater function in a queue and re-render your component. During the next render, React will calculate the next state by applying all of the queued updaters to the previous state. [See an example below.](#updating-state-based-on-the-previous-state)
#### Returns {/*setstate-returns*/}
`set` functions do not have a return value.
#### Caveats {/*setstate-caveats*/}
* The `set` function **only updates the state variable for the *next* render**. If you read the state variable after calling the `set` function, [you will still get the old value](#ive-updated-the-state-but-logging-gives-me-the-old-value) that was on the screen before your call.
* If the new value you provide is identical to the current `state`, as determined by an [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison, React will **skip re-rendering the component and its children.** This is an optimization. Although in some cases React may still need to call your component before skipping the children, it shouldn't affect your code.
* React [batches state updates](/learn/queueing-a-series-of-state-updates). It updates the screen **after all the event handlers have run** and have called their `set` functions. This prevents multiple re-renders during a single event. In the rare case that you need to force React to update the screen earlier, for example to access the DOM, you can use [`flushSync`](/apis/flushsync).
* Calling the `set` function *during rendering* is only allowed from within the currently rendering component. React will discard its output and immediately attempt to render it again with the new state. This pattern is rarely needed, but you can use it to **store information from the previous renders**. [See an example below.](#storing-information-from-previous-renders)
* In Strict Mode, React will **call your updater function twice** in order to [help you find accidental impurities](#my-initializer-or-updater-function-runs-twice). This is development-only behavior and does not affect production. If your updater function is pure (as it should be), this should not affect the logic of your component. The result from one of the calls will be ignored.
---
## Usage {/*usage*/}
### Adding state to a component {/*adding-state-to-a-component*/}
First, declare the state variables you need. Then, update them on interaction and display them in your JSX.
<APIAnatomy>
<AnatomyStep title="Declare a state variable">
Call `useState` and pass the initial state to it. React will store the state that you passed, and give it back to you.
React will store the next state, render your component again with the new values, and update the UI.
</AnatomyStep>
<Gotcha>
<AnatomyStep title="Set state on interaction">
Calling the `set` function [**does not** change the current state in the already executing code](#ive-updated-the-state-but-logging-gives-me-the-old-value):
To change the state, call the state setter function with the next state value. React will put that value into state instead.
</AnatomyStep>
<AnatomyStep title="Render state in the UI">
Use the state in your JSX or component logic.
</AnatomyStep>
```js [[1, 4, "const [count, setCount] = useState(0);"], [2, 7, "setCount(count + 1);"], [3, 12, "count"]]
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
You pressed me {count} times
</button>
);
```js {3}
function handleClick() {
setName('Robin');
console.log(name); // Still "Taylor"!
}
```
</APIAnatomy>
It only affects what `useState` will return starting from the *next* render.
</Gotcha>
<Recipes titleText="Basic useState examples" titleId="examples-basic">
@ -257,7 +187,7 @@ import { useState } from 'react';
export default function Form() {
const [name, setName] = useState('Taylor');
const [age, setAge] = useState(28);
const [age, setAge] = useState(42);
return (
<>
@ -266,7 +196,7 @@ export default function Form() {
onChange={e => setName(e.target.value)}
/>
<button onClick={() => setAge(age + 1)}>
Happy birthday!
Increment age
</button>
<p>Hello, {name}. You are {age}.</p>
</>
@ -284,54 +214,63 @@ button { display: block; margin-top: 10px; }
</Recipes>
Read [state as a component's memory](/learn/state-a-components-memory) to learn more.
---
### Updating state based on the previous state {/*updating-state-based-on-the-previous-state*/}
This code looks like it increments the counter three times, but it only increments once:
Suppose the `age` is `42`. This handler calls `setAge(age + 1)` three times:
```js
const [count, setCount] = useState(0);
function handleClick() {
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
}
```
However, after one click, `age` will only be `43` rather than `45`! This is because calling the `set` function [does not update](/learn/state-as-a-snapshot) the `age` state variable in the already running code. So each `setAge(age + 1)` call becomes `setAge(43)`.
To solve this problem, **you may pass an *updater function*** to `setAge` instead of the next state:
```js [[1, 2, "a", 0], [2, 2, "a + 1"], [1, 3, "a", 0], [2, 3, "a + 1"], [1, 4, "a", 0], [2, 4, "a + 1"]]
function handleClick() {
setCount(count + 1); // setCount(0 + 1)
setCount(count + 1); // setCount(0 + 1)
setCount(count + 1); // setCount(0 + 1)
setAge(a => a + 1); // setAge(42 => 43)
setAge(a => a + 1); // setAge(43 => 44)
setAge(a => a + 1); // setAge(44 => 45)
}
```
This is because calling `setCount` will not update the `count` until the next render, so each call becomes `setCount(0 + 1)`. **To make a series of state changes, each of which depends on the previous state,** pass an *updater function* to `setCount`. React will queue them and run them all to calculate state on next render.
Here, `a => a + 1` is your updater function. It takes the <CodeStep step={1}>pending state</CodeStep> and calculates the <CodeStep step={2}>next state</CodeStep> from it.
React puts your updater functions in a [queue](/learn/queueing-a-series-of-state-updates). Then, during the next render, it will call them in the same order:
<APIAnatomy>
1. `a => a + 1` will receive `42` as the pending state and return `43` as the next state.
1. `a => a + 1` will receive `43` as the pending state and return `44` as the next state.
1. `a => a + 1` will receive `44` as the pending state and return `45` as the next state.
<AnatomyStep title="You receive: Pending state">
There are no other queued updates, so React will store `45` as the current state in the end.
You get the latest state with the previously queued updates applied to it. For example, if `count` was `0` and you call `setCount(c => c + 1)` three times in a row, then the pending `c` state will be `0` in the first updater, `1` in the second one, and `2` in the third one, and `3` is the final state.
By convention, it's common to name the pending state argument for the first letter of the state variable name, like `a` for `age`. However, you may also call it like `prevAge` or something else that you find clearer.
</AnatomyStep>
React may [call your updaters twice](#my-initializer-or-updater-function-runs-twice) in development to verify that they are [pure](/learn/keeping-components-pure).
<AnatomyStep title="You return: Next state">
<DeepDive title="Is using an updater always preferred?">
You return the next state you want to see on the screen.
You might hear a recommendation to always write code like `setAge(a => a + 1)` if the state you're setting is calculated from the previous state. There is no harm in it, but it is also not always necessary.
</AnatomyStep>
In most cases, there is no difference between these two approaches. React always makes sure that for intentional user actions, like clicks, the `age` state variable would be updated before the next click. This means there is no risk of a click handler seeing a "stale" `age` at the beginning of the event handler.
```js [[1, 3, "c", 0], [2, 3, "c + 1"], [1, 6, "c", 0], [2, 6, "c + 1"], [1, 9, "c", 0], [2, 9, "c + 1"]]
function handleClick() {
// 0 => 1
setCount(c => c + 1);
However, if you do multiple updates within the same event, updaters can be helpful. They're also helpful if accessing the state variable itself is inconvenient (you might run into this when optimizing re-renders).
// 1 => 2
setCount(c => c + 1);
If you prefer consistency over slightly more verbose syntax, it's reasonable to always write an updater if the state you're setting is calculated from the previous state. If it's calculated from the previous state of some *other* state variable, you might want to combine them into one object and [use a reducer](/learn/extracting-state-logic-into-a-reducer).
// 2 => 3
setCount(c => c + 1);
}
```
</DeepDive>
<Recipes titleText="The difference between passing an updater and passing the next state directly" titleId="examples-updater">
### Passing the updater function {/*passing-the-updater-function*/}
</APIAnatomy>
This example passes the updater function, so the "+3" button works.
<Sandpack>
@ -339,49 +278,81 @@ function handleClick() {
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
const [age, setAge] = useState(42);
function handleClick() {
setCount(c => c + 1);
setCount(c => c + 1);
setCount(c => c + 1);
console.log(count); // Still 0 until next render!
function increment() {
setAge(a => a + 1);
}
return (
<>
<h1>{count}</h1>
<button onClick={handleClick}>+3</button>
<h1>Your age: {age}</h1>
<button onClick={() => {
increment();
increment();
increment();
}}>+3</button>
<button onClick={() => {
increment();
}}>+1</button>
</>
);
}
```
```css
button { display: inline-block; margin: 10px; font-size: 20px; }
h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }
button { display: block; margin: 10px; font-size: 20px; }
h1 { display: block; margin: 10px; }
```
</Sandpack>
React may [call your updater function twice](#my-initializer-or-updater-function-runs-twice) in development.
<Solution />
Read [state as a snapshot](/learn/state-as-a-snapshot) and [queueing a series of state changes](/learn/queueing-a-series-of-state-updates) to learn more.
### Passing the next state directly {/*passing-the-next-state-directly*/}
<DeepDive title="Is using an updater always preferred?">
This example **does not** pass the updater function, so the "+3" button **doesn't work as intended**.
You might hear a recommendation to always write code like `setCount(c => c + 1)` if the state you're setting is calculated from the previous state. There is no harm in it, but it is also not always necessary.
<Sandpack>
In most cases, there is no difference between these two approaches. React always makes sure that for intentional user actions, like clicks, the `count` state variable would be updated before the next click. This means there is no risk of a click handler seeing a "stale" `count` at the beginning of the event handler.
```js
import { useState } from 'react';
However, if you do multiple updates within the same event, updaters can be helpful. They're also helpful if accessing the state variable itself is inconvenient (you might run into this when optimizing re-renders).
export default function Counter() {
const [age, setAge] = useState(42);
If you prefer consistency over slightly more verbose syntax, it's reasonable to always write an updater if the state you're setting is calculated from the previous state. If it's calculated from the previous state of some *other* state variable, you might want to combine them into one object and [use a reducer](/learn/extracting-state-logic-into-a-reducer).
function increment() {
setAge(age + 1);
}
</DeepDive>
return (
<>
<h1>Your age: {age}</h1>
<button onClick={() => {
increment();
increment();
increment();
}}>+3</button>
<button onClick={() => {
increment();
}}>+1</button>
</>
);
}
```
---
```css
button { display: block; margin: 10px; font-size: 20px; }
h1 { display: block; margin: 10px; }
```
</Sandpack>
<Solution />
</Recipes>
---
### Updating objects and arrays in state {/*updating-objects-and-arrays-in-state*/}
@ -401,6 +372,8 @@ setForm({
});
```
Read [updating objects in state](/learn/updating-objects-in-state) and [updating arrays in state](/learn/updating-arrays-in-state) to learn more.
<Recipes titleText="Examples of objects and arrays in state" titleId="examples-objects">
### Form (object) {/*form-object*/}
@ -844,40 +817,90 @@ function ItemList({ artworks, onToggle }) {
</Recipes>
Read [updating objects in state](/learn/updating-objects-in-state) and [updating arrays in state](/learn/updating-arrays-in-state) to learn more.
---
### Avoiding recreating the initial state {/*avoiding-recreating-the-initial-state*/}
The initial state that you pass to `useState` is only used for the initial render. For the next renders, this argument is ignored. If creating the initial state is expensive, it is wasteful to create and throw it away on every render. To avoid this, **you can pass an *initializer function* to `useState`.** React will only run it during the initialization to calculate the initial state, but won't run it for re-renders. This is a performance optimization.
React saves the initial state once and ignores it on the next renders.
<APIAnatomy>
```js
function TodoList() {
const [todos, setTodos] = useState(createInitialTodos());
// ...
```
<AnatomyStep title="Initializing state with a value">
Although the result of `createInitialTodos()` is only used for the initial render, you're still calling this function on every render. This can be wasteful if it's creating large arrays or performing expensive calculations.
Most often, you will provide the initial state during render.
To solve this, you may **pass it as an _initializer_ function** to `useState` instead:
</AnatomyStep>
```js
function TodoList() {
const [todos, setTodos] = useState(createInitialTodos);
// ...
```
<AnatomyStep title="Initializing state with a function">
Notice that you’re passing `createInitialTodos`, which is the *function itself*, and not `createInitialTodos()`, which is the result of calling it. If you pass a function to `useState`, React will only call it during initialization.
But you can also give React a function that calculates the initial state instead. React will only call that function when initializing the component, and won't call it again.
React may [call your initializers twice](#my-initializer-or-updater-function-runs-twice) in development to verify that they are [pure](/learn/keeping-components-pure).
</AnatomyStep>
<Recipes titleText="The difference between passing an initializer and passing the initial state directly" titleId="examples-initializer">
```js [[1, 2, "''"], [2, 5, "() => createInitialTodos()"]]
function TodoList() {
### Passing the initializer function {/*passing-the-initializer-function*/}
This example passes the initializer function, so the `createInitialTodos` function only runs during initialization. It does not run when component re-renders, such as when you type into the input.
<Sandpack>
```js
import { useState } from 'react';
function createInitialTodos() {
const initialTodos = [];
for (let i = 0; i < 50; i++) {
initialTodos.push({
id: i,
text: 'Item ' + (i + 1)
});
}
return initialTodos;
}
export default function TodoList() {
const [todos, setTodos] = useState(createInitialTodos);
const [text, setText] = useState('');
const [todos, setTodos] = useState(
() => createInitialTodos()
return (
<>
<input
value={text}
onChange={e => setText(e.target.value)}
/>
<button onClick={() => {
setText('');
setTodos([{
id: todos.length,
text: text
}, ...todos]);
}}>Add</button>
<ul>
{todos.map(item => (
<li key={item.id}>
{item.text}
</li>
))}
</ul>
</>
);
// ...
}
```
</APIAnatomy>
</Sandpack>
<Solution />
### Passing the initial state directly {/*passing-the-initial-state-directly*/}
This example **does not** pass the initializer function, so the `createInitialTodos` function runs on every render, such as when you type into the input. There is no observable difference in behavior, but this code is less efficient.
<Sandpack>
@ -889,30 +912,46 @@ function createInitialTodos() {
for (let i = 0; i < 50; i++) {
initialTodos.push({
id: i,
text: 'Item #' + i
text: 'Item ' + (i + 1)
});
}
return initialTodos;
}
export default function TodoList() {
const [todos, setTodos] = useState(createInitialTodos);
const [todos, setTodos] = useState(createInitialTodos());
const [text, setText] = useState('');
return (
<ul>
{todos.map(item => (
<li key={item.id}>
{item.text}
</li>
))}
</ul>
<>
<input
value={text}
onChange={e => setText(e.target.value)}
/>
<button onClick={() => {
setText('');
setTodos([{
id: todos.length,
text: text
}, ...todos]);
}}>Add</button>
<ul>
{todos.map(item => (
<li key={item.id}>
{item.text}
</li>
))}
</ul>
</>
);
}
```
</Sandpack>
React may [call your initializer function twice](#my-initializer-or-updater-function-runs-twice) in development.
<Solution />
</Recipes>
---
@ -1042,6 +1081,81 @@ This pattern can be hard to understand and is usually best avoided. However, it'
---
## Reference {/*reference*/}
### `useState(initialState)` {/*usestate*/}
Call `useState` at the top level of your component to declare a [state variable](/learn/state-a-components-memory).
```js
import { useState } from 'react';
function MyComponent() {
const [age, setAge] = useState(28);
const [name, setName] = useState('Taylor');
const [todos, setTodos] = useState(() => createTodos());
// ...
```
The convention is to name state variables like `[something, setSomething]` using [array destructuring](/learn/a-javascript-refresher#array-destructuring).
[See more examples above.](#examples-basic)
#### Parameters {/*parameters*/}
* `initialState`: The value you want the state to be initially. It can be a value of any type, but there is a special behavior for functions. This argument is ignored after the initial render.
* If you pass a function as `initialState`, it will be treated as an _initializer function_. It should be pure, should take no arguments, and should return a value of any type. React will call your initializer function when initializing the component, and store its return value as the initial state. [See an example above.](#avoiding-recreating-the-initial-state)
#### Returns {/*returns*/}
`useState` returns an array with exactly two values:
1. The current state. During the first render, it will match the `initialState` you have passed.
2. The [`set` function](#setstate) that lets you update the state to a different value and trigger a re-render.
#### Caveats {/*caveats*/}
* `useState` is a Hook, so you can only call it **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a new component and move the state into it.
* In Strict Mode, React will **call your initializer function twice** in order to [help you find accidental impurities](#my-initializer-or-updater-function-runs-twice). This is development-only behavior and does not affect production. If your initializer function is pure (as it should be), this should not affect the logic of your component. The result from one of the calls will be ignored.
---
### `set` functions, like `setSomething(nextState)` {/*setstate*/}
The `set` function returned by `useState` lets you update the state to a different value and trigger a re-render. You can pass the next state directly, or a function that calculates it from the previous state:
```js
const [name, setName] = useState('Edward');
function handleClick() {
setName('Taylor');
setAge(a => a + 1);
// ...
```
#### Parameters {/*setstate-parameters*/}
* `nextState`: The value that you want the state to be. It can be a value of any type, but there is a special behavior for functions.
* If you pass a function as `nextState`, it will be treated as an _updater function_. It must be pure, should take the pending state as its only argument, and should return the next state. React will put your updater function in a queue and re-render your component. During the next render, React will calculate the next state by applying all of the queued updaters to the previous state. [See an example above.](#updating-state-based-on-the-previous-state)
#### Returns {/*setstate-returns*/}
`set` functions do not have a return value.
#### Caveats {/*setstate-caveats*/}
* The `set` function **only updates the state variable for the *next* render**. If you read the state variable after calling the `set` function, [you will still get the old value](#ive-updated-the-state-but-logging-gives-me-the-old-value) that was on the screen before your call.
* If the new value you provide is identical to the current `state`, as determined by an [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison, React will **skip re-rendering the component and its children.** This is an optimization. Although in some cases React may still need to call your component before skipping the children, it shouldn't affect your code.
* React [batches state updates](/learn/queueing-a-series-of-state-updates). It updates the screen **after all the event handlers have run** and have called their `set` functions. This prevents multiple re-renders during a single event. In the rare case that you need to force React to update the screen earlier, for example to access the DOM, you can use [`flushSync`](/apis/flushsync).
* Calling the `set` function *during rendering* is only allowed from within the currently rendering component. React will discard its output and immediately attempt to render it again with the new state. This pattern is rarely needed, but you can use it to **store information from the previous renders**. [See an example above.](#storing-information-from-previous-renders)
* In Strict Mode, React will **call your updater function twice** in order to [help you find accidental impurities](#my-initializer-or-updater-function-runs-twice). This is development-only behavior and does not affect production. If your updater function is pure (as it should be), this should not affect the logic of your component. The result from one of the calls will be ignored.
---
## Troubleshooting {/*troubleshooting*/}
### I've updated the state, but logging gives me the old value {/*ive-updated-the-state-but-logging-gives-me-the-old-value*/}
@ -1160,4 +1274,28 @@ setTodos(prevTodos => {
Now that this updater function is pure, calling it an extra time doesn't make a difference in behavior. This is why React calling it twice helps you find mistakes. **Only component, initializer, and updater functions need to be pure.** Event handlers don't need to be pure, so React will never call your event handlers twice.
Read [keeping components pure](/learn/keeping-components-pure) to learn more.
Read [keeping components pure](/learn/keeping-components-pure) to learn more.
---
### I'm trying to set state to a function, but it gets called instead {/*im-trying-to-set-state-to-a-function-but-it-gets-called-instead*/}
You can't put a function into state like this:
```js
const [fn, setFn] = useState(someFunction);
function handleClick() {
setFn(someOtherFunction);
}
```
Because you're passing a function, React assumes that `someFunction` is an [initializer function](#avoiding-recreating-the-initial-state), and that `someOtherFunction` is an [updater function](#updating-state-based-on-the-previous-state), so it tries to call them and store the result. To actually *store* a function, you have to put `() =>` before them in both cases. Then React will store the functions you pass.
```js {1,4}
const [fn, setFn] = useState(() => someFunction);
function handleClick() {
setFn(() => someOtherFunction);
}
```
Loading…
Cancel
Save