From 176b93c964aa843ecc0a8ae0a573a657fdc41db0 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 15 Sep 2022 05:37:01 +0100 Subject: [PATCH] [Beta] Higher-level comments in useCallback --- beta/src/content/apis/react/useCallback.md | 14 ++++++------- beta/src/content/apis/react/useMemo.md | 24 +++++++++------------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/beta/src/content/apis/react/useCallback.md b/beta/src/content/apis/react/useCallback.md index 2675488d6..8ea8867a3 100644 --- a/beta/src/content/apis/react/useCallback.md +++ b/beta/src/content/apis/react/useCallback.md @@ -153,8 +153,8 @@ function ProductPage({ productId, referrer }) { The difference is in *what* they're letting you cache: -* **[`useMemo`](/apis/react/useMemo) caches the *result* of calling your function.** In this example, it caches the result of calling `computeRequirements(product)` so that it doesn't change unless `product` has changed. This lets you pass the `requirements` object down without unnecessarily re-rendering `ShippingForm`. When necessary, React will call the function you've passed during rendering to calculate the result. -* **`useCallback` caches *the function itself.*** Unlike `useMemo`, it does not call the function you provide. Instead, it caches the function you provided so that `handleSubmit` *itself* doesn't change unless `product` or `referrerId` has changed. This lets you pass the `handleSubmit` function down without unnecessarily re-rendering `ShippingForm`. Your code won't be called until the user submits the form. +* **[`useMemo`](/apis/react/useMemo) caches the *result* of calling your function.** In this example, it caches the result of calling `computeRequirements(product)` so that it doesn't change unless `productId` has changed. This lets you pass the `requirements` object down without unnecessarily re-rendering `ShippingForm`. When necessary, React will call the function you've passed during rendering to calculate the result. +* **`useCallback` caches *the function itself.*** Unlike `useMemo`, it does not call the function you provide. Instead, it caches the function you provided so that `handleSubmit` *itself* doesn't change unless `productId` or `referrer` has changed. This lets you pass the `handleSubmit` function down without unnecessarily re-rendering `ShippingForm`. Your code won't be called until the user submits the form. If you're already familiar with [`useMemo`,](/apis/react/useMemo) you might find it helpful to think of `useCallback` as this: @@ -200,7 +200,9 @@ If a specific interaction still feels laggy, [use the React Developer Tools prof In this example, the `ShippingForm` component is **artificially slowed down** so that you can see what happens when a React component you're rendering is genuinely slow. Try incrementing the counter and toggling the theme. -When you increment the counter, the `ShippingForm` re-renders. Since its rendering is artificially slowed down, the interaction feels slow. Then try toggling the theme. You'll notice that toggling the theme is fast because the slowed-down `ShippingForm` component skips re-rendering. It is able to skip re-rendering because it's wrapped in [`memo`](/apis/react/memo) *and* the props passed to it are the same as during the last render. Specifically, the `handleSubmit` function does not change between the re-renders thanks to `useCallback`. Its dependencies (`product` and `referrerId`) have not changed, so `useCallback` returns a cached function. +Incrementing the counter feels slow because it forces the slowed down `ShippingForm` to re-render. That's expected because the counter has changed, and so you need to reflect the user's new choice on the screen. + +Next, try toggling the theme. **Thanks to `useCallback` together with [`memo`](/apis/react/memo), it’s fast despite the artificial slowdown!** `ShippingForm` skipped re-rendering because the `handleSubmit` function has not changed. The `handleSubmit` function has not changed because both `productId` and `referral` (your `useCallback` dependencies) haven't changed since last render. @@ -338,11 +340,9 @@ button[type="button"] { #### 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 `useCallback` call. +In this example, the `ShoppingForm` implementation is also **artificially slowed down** so that you can see what happens when some React component you're rendering is genuinely slow. Try incrementing the counter and toggling the theme. -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 `ProductPage` component re-renders too and creates a new `handleSubmit` function. Creating a function by itself is not a problem, but it passes this function down to the **artificially slowed down** `ShippingForm` component. Although `ShippingForm` is wrapped in [`memo`,](/apis/react/memo) it can't skip re-rendering because its `onSubmit` prop is different from the last time. Toggling the theme feels slow even though `ShippingForm` doesn't use `theme`. +Unlike in the previous example, toggling the theme is also slow now! This is because **there is no `useMemo` call in this version,** so `handleSubmit` is always a new function, and the slowed down `ShoppingForm` component can't skip re-rendering. diff --git a/beta/src/content/apis/react/useMemo.md b/beta/src/content/apis/react/useMemo.md index 795d87611..7df0b440f 100644 --- a/beta/src/content/apis/react/useMemo.md +++ b/beta/src/content/apis/react/useMemo.md @@ -117,9 +117,9 @@ If a specific interaction still feels laggy, [use the React Developer Tools prof 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)) +Switching the tabs feels slow because it forces the slowed down `filterTodos` to re-execute. That's expected because the `tab` has changed, and so the entire calculation *needs* to re-run. (If you're curious why it runs twice, it's explained [here.](#my-calculation-runs-twice-on-every-re-render)) -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. +Next, try toggling the theme. **Thanks to `useMemo`, it's fast despite the artificial slowdown!** The slow `filterTodos` call was skipped because both `todos` and `tab` (which you pass as dependencies to `useMemo`) haven't changed since the last render. @@ -247,11 +247,9 @@ label { #### Always recalculating a value {/*always-recalculating-a-value*/} -This example is the same as the previous one, but it doesn't have a `useMemo` call. +In this example, the `filterTodos` implementation is also **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. -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 you call `filterTodos` every time. +Unlike in the previous example, toggling the theme is also slow now! This is because **there is no `useMemo` call in this version,** so the artificially slowed down `filterTodos` gets called on every re-render. It is called even if only `theme` has changed. @@ -371,7 +369,7 @@ label { -However, here is the same code **with the artificial slowdown removed:** +However, here is the same code **with the artificial slowdown removed.** Give it a try! Does it feel fast despite no `useMemo`? @@ -576,9 +574,9 @@ Manually wrapping JSX nodes into `useMemo` is not convenient. For example, you c 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. +Switching the tabs feels slow because it forces the slowed down `List` to re-render. That's expected because the `tab` has changed, and so you need to reflect the user's new choice on the screen. -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`. +Next, try toggling the theme. **Thanks to `useMemo` together with [`memo`](/apis/react/memo), it’s fast despite the artificial slowdown!** The `List` skipped re-rendering because the `visibleItems` array has not changed since the last render. The `visibleItems` array has not changed because both `todos` and `tab` (which you pass as dependencies to `useMemo`) haven't changed since the last render. @@ -718,11 +716,9 @@ label { #### 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. +In this example, the `List` implementation is also **artificially slowed down** so that you can see what happens when some React component you're rendering is genuinely slow. Try switching the tabs and toggling the theme. -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. +Unlike in the previous example, toggling the theme is also slow now! This is because **there is no `useMemo` call in this version,** so the `visibleTodos` is always a different array, and the slowed down `List` component can't skip re-rendering. @@ -854,7 +850,7 @@ label { -However, here is the same code **with the artificial slowdown removed:** +However, here is the same code **with the artificial slowdown removed.** Give it a try! Does it feel fast without `useMemo`?