diff --git a/beta/src/content/apis/react/index.md b/beta/src/content/apis/react/index.md
index 670501895..3a985a2b4 100644
--- a/beta/src/content/apis/react/index.md
+++ b/beta/src/content/apis/react/index.md
@@ -349,7 +349,9 @@ useInsertionEffect(() => {
Return a memoized callback.
```js
-const memoizedCallback = useCallback(callback, [...deps]);
+const handleClick = useCallback(() => {
+ doSomething(a, b);
+}, [a, b]);
```
@@ -359,7 +361,9 @@ const memoizedCallback = useCallback(callback, [...deps]);
Return a memoized value.
```js
-const memoizedValue = useMemo(() => value, [...deps]);
+const value = useMemo(() => {
+ return calculateValue(a, b);
+}, [a, b]);
```
diff --git a/beta/src/content/apis/react/useMemo.md b/beta/src/content/apis/react/useMemo.md
index 9595a9b4f..25f633246 100644
--- a/beta/src/content/apis/react/useMemo.md
+++ b/beta/src/content/apis/react/useMemo.md
@@ -2,19 +2,1183 @@
title: useMemo
---
-
-
-This section is incomplete, please see the old docs for [useMemo](https://reactjs.org/docs/hooks-reference.html#usememo).
-
-
-
-
+`useMemo` is a React Hook that lets you skip recalculating a value on a re-render.
+
```js
-const memoizedValue = useMemo(callback, [...dependencies])
+const value = useMemo(calculateValue, dependencies)
```
+
+---
+
+## Usage {/*usage*/}
+
+### Skipping expensive recalculations {/*skipping-expensive-recalculations*/}
+
+By default, React will re-run the entire body of your component every time that it re-renders. For example, if this `TodoList` updates its state or receives new props from its parent, the `filterTodos` function will re-run:
+
+```js {2}
+function TodoList({ todos, tab, theme }) {
+ const visibleTodos = filterTodos(todos, tab);
+ // ...
+}
+```
+
+**Usually, this isn't a problem because most calculations are very fast.** However, if you're filtering or transforming a large array, or doing some other expensive computation, you might want to skip doing it again unless something has changed. If both `todos` and `tab` are the same as they were during the last render, you can instruct React to reuse the `visibleTodos` you've already calculated during the last render. This technique is called *[memoization.](https://en.wikipedia.org/wiki/Memoization)*
+
+To declare a memoized value, wrap your calculation into a `useMemo` call at the top level of your component:
+
+```js [[3, 4, "visibleTodos"], [1, 4, "() => filterTodos(todos, tab)"], [2, 4, "[todos, tab]"]]
+import { useMemo } from 'react';
+
+function TodoList({ todos, tab, theme }) {
+ const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
+ // ...
+}
+```
+
+You need to pass two things to `useMemo`:
+
+1. A calculation function that takes no arguments, like `() =>`, and returns what you wanted to calculate.
+2. A list of dependencies including every value within your component that's used inside your calculation.
+
+On the initial render, the value you'll get from `useMemo` will be the result of calling your calculation.
+
+On every next render, React will compare the dependencies with the dependencies you passed during the last render. If none of the dependencies have changed (compared with [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)), `useMemo` will return the value you already calculated on the last render. Otherwise, React will re-run your calculation and return the new value.
+
+**You should only rely on this feature as a performance optimization.** If your code doesn't work at all without it, find the underlying problem and fix the code first, and only then add memoization to improve the performance.
+
+
+
+If your calculation doesn't fit on a single line, add curly braces and an explicit `return` statement:
+
+```js {2-5}
+function TodoList({ todos, tab, theme }) {
+ const visibleTodos = useMemo(() => {
+ const filtered = filterTodos(todos, tab);
+ return filtered;
+ }, [todos, tab]);
+ // ...
+}
+```
+
+This code is equivalent to the previous example.
+
+
+
+
+
+In general, unless you're creating or looping over thousands of objects, it's probably not expensive. If you want to get more confidence, you can add a console log to measure the time spent in a piece of code:
+
+```js {1,3}
+console.time('filter array');
+const visibleTodos = filterTodos(todos, tab);
+console.timeEnd('filter array');
+```
+
+Perform the interaction you're measuring (for example, typing into the input). You will then see logs like `filter array: 0.15ms` in your console. If the overall logged time adds up to a significant amount (say, `1ms` or more), it might make sense to memoize that calculation. As an experiment, you can then wrap the calculation in `useMemo` to verify whether the total logged time has decreased for that interaction or not:
+
+```js
+console.time('filter array');
+const visibleTodos = useMemo(() => {
+ return filterTodos(todos, tab); // Skipped if todos and tab haven't changed
+}, [todos, tab]);
+console.timeEnd('filter array');
+```
+
+`useMemo` won't make the *first* render faster. It only helps you skip unnecessary work on updates.
+
+Keep in mind that your machine is probably faster than your users' so it's a good idea to test the performance with an artificial slowdown. For example, Chrome offers a [CPU Throttling](https://developer.chrome.com/blog/new-in-devtools-61/#throttling) option for this.
+
+Also note that measuring performance in development will not give you the most accurate results. (For example, when [Strict Mode](/apis/react/StrictMode) is on, you will see each component render twice rather than once.) To get the most accurate timings, build your app for production and test it on a device like your users have.
+
+
+
+
+
+#### Skipping recalculation with `useMemo` {/*skipping-recalculation-with-usememo*/}
+
+In this example, the `filterTodos` implementation is **artificially slowed down** so that you can see what happens when some JavaScript function you're calling during rendering is genuinely slow. Try switching the tabs and toggling the theme.
+
+When you switch the tabs, `filterTodos` gets called. That's expected because the `tab` has changed. (It also gets called twice in development, but you should ignore this. React calls your components twice during development to help [find impure code.](/learn/keeping-components-pure))
+
+Notice that when you switch the theme toggle, `filterTodos` *does not* get called. This is because both `todos` and `tab` (which you pass as dependencies to `useMemo`) are the same as they were during the last render. This is what `useMemo` enables.
+
+
+
+```js App.js
+import { useState } from 'react';
+import { createTodos } from './utils.js';
+import TodoList from './TodoList.js';
+
+const todos = createTodos();
+
+export default function App() {
+ const [tab, setTab] = useState('all');
+ const [isDark, setIsDark] = useState(false);
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+}
+
+```
+
+```js TodoList.js active
+import { useMemo } from 'react';
+import { filterTodos } from './utils.js'
+
+export default function TodoList({ todos, theme, tab }) {
+ const visibleTodos = useMemo(
+ () => filterTodos(todos, tab),
+ [todos, tab]
+ );
+ return (
+
+ );
+}
+```
+
+```js utils.js
+export function createTodos() {
+ const todos = [];
+ for (let i = 0; i < 50; i++) {
+ todos.push({
+ id: i,
+ text: "Todo " + (i + 1),
+ completed: Math.random() > 0.5
+ });
+ }
+ return todos;
+}
+
+export function filterTodos(todos, tab) {
+ console.log('Filtering ' + todos.length + ' todos for "' + tab + '" tab.');
+
+ return todos.filter(todo => {
+ if (tab === 'all') {
+ return true;
+ } else if (tab === 'active') {
+ return !todo.completed;
+ } else if (tab === 'completed') {
+ return todo.completed;
+ }
+ });
+}
+```
+
+```css
+label {
+ display: block;
+ margin-top: 10px;
+}
+
+.dark {
+ background-color: black;
+ color: white;
+}
+
+.light {
+ background-color: white;
+ color: black;
+}
+```
+
+
+
+Quite often, code without memoization works fine. If your interactions are fast enough, you might not need memoization.
+
+You can try increasing the number of todo items in `utils.js` and see how the behavior changes. This particular calculation wasn't very expensive to begin with, but if the number of todos grows significantly, most of the overhead will be in re-rendering rather than in the filtering. Keep reading below to see how you can optimize re-rendering with `useMemo`.
+
+
+
+
+
+---
+
+### Skipping re-rendering of components {/*skipping-re-rendering-of-components*/}
+
+By default, when a component re-renders, React re-renders all of its children recursively. This is fine for components that don't require much calculation to re-render. Components higher up the tree or slower components can opt into *skipping re-renders when their props are the same* by wrapping themselves in [`memo`](/apis/react/memo):
+
+```js {1,7}
+import { memo } from 'react';
+
+function List({ items }) {
+ // ...
+}
+
+export default memo(List);
+```
+
+For this optimization to work, the parent component that renders this `` needs to ensure that, if it doesn't want `List` to re-render, every prop it passes to the `List` must be the same as on the last render.
+
+Let's say the parent `TodoList` component looks like this:
+
+```js {2,5}
+export default function TodoList({ todos, tab, theme }) {
+ const visibleTodos = filterTodos(todos, tab);
+ return (
+
+
+
+ );
+}
+```
+
+With the above code, the `List` optimization will not work because `visibleTodos` will be a different array on every re-render of the `TodoList` component. To fix it, wrap the calculation of `visibleTodos` in `useMemo`:
+
+```js {2,5}
+export default function TodoList({ todos, tab, theme }) {
+ const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
+ return (
+
+
+
+ );
+}
+```
+
+After this change, as long as `todos` and `tab` haven't changed, thanks to `useMemo`, the `visibleTodos` won't change between re-renders. Since `List` is wrapped in [`memo`](/apis/react/memo), it will only re-render if one of its props is different from its value on the last render. You're passing the same `items` prop, so `List` can skip the re-rendering entirely.
+
+Notice that in this example, it doesn't matter whether `filterTodos` itself is fast or slow. The point isn't to avoid a *slow calculation,* but it's to avoid *passing a different prop value every time* since that would break the [`memo`](/apis/react/memo) optimization of the child `List` component. The `useMemo` call in the parent makes `memo` work for the child.
+
+
+
+#### Skipping re-rendering with `useMemo` and `memo` {/*skipping-re-rendering-with-usememo-and-memo*/}
+
+In this example, the `List` component is **artificially slowed down** so that you can see what happens when a React component you're rendering is genuinely slow. Try switching the tabs and toggling the theme.
+
+When you switch the tabs, `` gets re-rendered. Changing the `tab` causes the `visibleTodos` to be recreated. Since the `items` passed to the `List` are a different array from the `items` passed to `List` on last render, the `List` must re-render.
+
+However, when you switch the theme toggle, `` *does not* re-render. This is because both `todos` and `tab` (which you pass as dependencies to `useMemo`) are the same as they were during the last render. This makes the `visibleTodos` the same as on the last render. In `List.js`, the `List` component is wrapped in [`memo`](/apis/react/memo), so it skips re-rendering for the same `items`.
+
+
+
+```js App.js
+import { useState } from 'react';
+import { createTodos } from './utils.js';
+import TodoList from './TodoList.js';
+
+const todos = createTodos();
+
+export default function App() {
+ const [tab, setTab] = useState('all');
+ const [isDark, setIsDark] = useState(false);
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+}
+```
+
+```js TodoList.js active
+import { useMemo } from 'react';
+import List from './List.js';
+import { filterTodos } from './utils.js'
+
+export default function TodoList({ todos, theme, tab }) {
+ const visibleTodos = useMemo(
+ () => filterTodos(todos, tab),
+ [todos, tab]
+ );
+ return (
+
+
+
+ );
+}
+```
+
+```js List.js
+import { memo } from 'react';
+
+function List({ items }) {
+ console.log('[ARTIFICIALLY SLOW] Rendering with ' + items.length + ' items');
+
+ let startTime = performance.now();
+ while (performance.now() - startTime < 500) {
+ // do nothing to emulate a slow computer
+ }
+
+ return (
+
+ );
+}
+
+export default memo(List);
+```
+
+```js utils.js
+export function createTodos() {
+ const todos = [];
+ for (let i = 0; i < 50; i++) {
+ todos.push({
+ id: i,
+ text: "Todo " + (i + 1),
+ completed: Math.random() > 0.5
+ });
+ }
+ return todos;
+}
+
+export function filterTodos(todos, tab) {
+ return todos.filter(todo => {
+ if (tab === 'all') {
+ return true;
+ } else if (tab === 'active') {
+ return !todo.completed;
+ } else if (tab === 'completed') {
+ return todo.completed;
+ }
+ });
+}
+```
+
+```css
+label {
+ display: block;
+ margin-top: 10px;
+}
+
+.dark {
+ background-color: black;
+ color: white;
+}
+
+.light {
+ background-color: white;
+ color: black;
+}
+```
+
+
+
+
+
+#### Always re-rendering a component {/*always-re-rendering-a-component*/}
+
+This example is the same as the previous one, but it doesn't have a `useMemo` call.
+
+Try switching the theme in this example. It should feel much slower than the first one!
+
+When you toggle the theme, the `App` component re-renders. The `TodoList` component re-renders too and receives the next props with the updated theme. You havenβt wrapped the `filterTodos` call in `useMemo`, so `visibleTodos` is a different array on a re-render. When you pass the always-different `visibleTodos` to the `List` component, it has to re-render every time.
+
+
+
+```js App.js
+import { useState } from 'react';
+import { createTodos } from './utils.js';
+import TodoList from './TodoList.js';
+
+const todos = createTodos();
+
+export default function App() {
+ const [tab, setTab] = useState('all');
+ const [isDark, setIsDark] = useState(false);
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+}
+```
+
+```js TodoList.js active
+import List from './List.js';
+import { filterTodos } from './utils.js'
+
+export default function TodoList({ todos, theme, tab }) {
+ const visibleTodos = filterTodos(todos, tab);
+ return (
+
+
+
+ );
+}
+```
+
+```js List.js
+import { memo } from 'react';
+
+function List({ items }) {
+ console.log('[ARTIFICIALLY SLOW] Rendering with ' + items.length + ' items');
+
+ let startTime = performance.now();
+ while (performance.now() - startTime < 500) {
+ // do nothing to emulate a slow computer
+ }
+
+ return (
+