Browse Source

[beta] Remove 'each next' and 'every next' (#5063)

* [beta] Remove 'each next' and 'every next'

* Update beta/src/content/apis/react/useCallback.md

Co-authored-by: Ricky <2440089+rickhanlonii@users.noreply.github.com>

* Update separating-events-from-effects.md

* Update you-might-not-need-an-effect.md

Co-authored-by: Ricky <2440089+rickhanlonii@users.noreply.github.com>
Co-authored-by: dan <dan.abramov@gmail.com>
main
David McCabe 2 years ago
committed by GitHub
parent
commit
4417760473
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      beta/src/content/apis/react/useCallback.md
  2. 7
      beta/src/content/apis/react/useMemo.md
  3. 28
      beta/src/content/learn/reusing-logic-with-custom-hooks.md
  4. 18
      beta/src/content/learn/separating-events-from-effects.md
  5. 13
      beta/src/content/learn/you-might-not-need-an-effect.md

2
beta/src/content/apis/react/useCallback.md

@ -80,7 +80,7 @@ You need to pass two things to `useCallback`:
On the initial render, the <CodeStep step={3}>returned function</CodeStep> you'll get from `useCallback` will be the function you passed.
On every next render, React will compare the <CodeStep step={2}>dependencies</CodeStep> 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)), `useCallback` will return the function you passed on the *last* render. Otherwise, React will return the function you passed on *this* render.
On every render, React will compare the <CodeStep step={2}>dependencies</CodeStep> with the dependencies you passed during the previous render. If this is the first render, or any of the dependencies have changed (compared with [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)), `useCallback` will return the function you passed on *this* render. Otherwise, `useCallback` will return the function you passed on the *previous* render.
In other words, `useCallback` will cache your function, and return it on re-renders until the dependencies change. If both `product` and `referrerId` are the same as before, the `ProductPage` will pass the *same* `handleSubmit` function to the `ShippingForm`. The `ShippingForm` is wrapped in [`memo`](/apis/react/memo), so it will skip a render with same props.

7
beta/src/content/apis/react/useMemo.md

@ -49,7 +49,7 @@ You need to pass two things to `useMemo`:
On the initial render, the <CodeStep step={3}>value</CodeStep> you'll get from `useMemo` will be the result of calling your <CodeStep step={1}>calculation</CodeStep>.
On every next render, React will compare the <CodeStep step={2}>dependencies</CodeStep> 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.
On every subsequent render, React will compare the <CodeStep step={2}>dependencies</CodeStep> 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.
In other words, `useMemo` will cache your function's result, and return it on re-renders until the dependencies change. If both `todos` and `tab` are the same as before, the `TodoList` won't have to recalculate `visibleTodos`.
@ -552,7 +552,7 @@ 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, `<List />` 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.
When you switch the tabs, `<List />` 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, `<List />` *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`.
@ -1389,6 +1389,3 @@ function Report({ item }) {
}
Report = memo(Report); // ✅ Memoize individual items
```

28
beta/src/content/learn/reusing-logic-with-custom-hooks.md

@ -46,7 +46,7 @@ export default function StatusBar() {
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
window.removeEventListener('offline', handleOffline);
};
}, []);
@ -80,7 +80,7 @@ export default function SaveButton() {
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
window.removeEventListener('offline', handleOffline);
};
}, []);
@ -143,7 +143,7 @@ function useOnlineStatus() {
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
@ -202,7 +202,7 @@ export function useOnlineStatus() {
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
@ -628,7 +628,7 @@ export default function ChatRoom({ roomId }) {
useChatRoom({
roomId: roomId,
serverUrl: serverUrl
serverUrl: serverUrl
});
return (
@ -686,7 +686,7 @@ export default function ChatRoom({ roomId }) {
useChatRoom({
roomId: roomId,
serverUrl: serverUrl
serverUrl: serverUrl
});
return (
@ -815,7 +815,7 @@ export default function ChatRoom({ roomId }) {
useChatRoom({
roomId: roomId,
serverUrl: serverUrl
serverUrl: serverUrl
});
// ...
```
@ -828,7 +828,7 @@ export default function ChatRoom({ roomId }) {
useChatRoom({
roomId: roomId,
serverUrl: serverUrl
serverUrl: serverUrl
});
// ...
```
@ -1337,7 +1337,7 @@ export function useOnlineStatus() {
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
@ -1873,7 +1873,7 @@ html, body { min-height: 300px; }
font-size: 50px;
background-image: radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%);
animation: fadeIn 1000ms;
animation: fadeIn 1000ms;
}
@keyframes fadeIn {
@ -2161,7 +2161,7 @@ export function useInterval(onTick, delay) {
useEffect(() => {
const id = setInterval(onTick, delay);
return () => clearInterval(id);
}, [onTick, delay]);
}, [onTick, delay]);
}
```
@ -2187,7 +2187,7 @@ For some reason, the callback that updates the page background never runs. Add s
console.log('❌ Clearing an interval with delay ', delay)
clearInterval(id);
};
}, [onTick, delay]);
}, [onTick, delay]);
```
Do the logs match what you expect to happen? If some of your Effects seem to re-synchronize unnecessarily, can you guess which dependency is causing that to happen? Is there some way to [remove that dependency](/learn/removing-effect-dependencies) from your Effect?
@ -2241,7 +2241,7 @@ export function useInterval(onTick, delay) {
return () => {
clearInterval(id);
};
}, [onTick, delay]);
}, [onTick, delay]);
}
```
@ -2345,7 +2345,7 @@ export function useEvent(fn) {
In this example, the `usePointerPosition()` Hook tracks the current pointer position. Try moving your cursor or your finger over the preview area and see the red dot follow your movement. Its position is saved in the `pos1` variable.
In fact, there are five (!) different red dots being rendered. You don't see them because currently they all appear at the same position. This is what you need to fix. What you want to implement instead is a "staggered" movement: each next dot should "follow" the previous dot's path. For example, if you quickly move your cursor, the first dot should follow it immediately, the second dot should follow the first dot with a small delay, the third dot should follow the second dot, and so on.
In fact, there are five (!) different red dots being rendered. You don't see them because currently they all appear at the same position. This is what you need to fix. What you want to implement instead is a "staggered" movement: each dot should "follow" the previous dot's path. For example, if you quickly move your cursor, the first dot should follow it immediately, the second dot should follow the first dot with a small delay, the third dot should follow the second dot, and so on.
You need to implement the `useDelayedValue` custom Hook. Its current implementation returns the `value` provided to it. Instead, you want to return the value back from `delay` milliseconds ago. You might need some state and an Effect to do this.

18
beta/src/content/learn/separating-events-from-effects.md

@ -326,7 +326,7 @@ export default function App() {
<hr />
<ChatRoom
roomId={roomId}
theme={isDark ? 'dark' : 'light'}
theme={isDark ? 'dark' : 'light'}
/>
</>
);
@ -514,7 +514,7 @@ export default function App() {
<hr />
<ChatRoom
roomId={roomId}
theme={isDark ? 'dark' : 'light'}
theme={isDark ? 'dark' : 'light'}
/>
</>
);
@ -774,7 +774,7 @@ export default function App() {
<label>
<input type="checkbox"
checked={canMove}
onChange={e => setCanMove(e.target.checked)}
onChange={e => setCanMove(e.target.checked)}
/>
The dot is allowed to move
</label>
@ -809,7 +809,7 @@ The problem with the this code is in suppressing the dependency linter. If you r
The author of the original code has "lied" to React by saying that the Effect does not depend (`[]`) on any reactive values. This is why React did not re-synchronize the Effect after `canMove` has changed (and `handleMove` with it). Because React did not re-synchronize the Effect, the `handleMove` attached as a listener is the `handleMove` function created during the initial render. During the initial render, `canMove` was `true`, which is why `handleMove` from the initial render will forever see that value.
**If you never suppress the linter, you will never see problems with stale values.**
**If you never suppress the linter, you will never see problems with stale values.**
With `useEvent`, there is no need to "lie" to the linter, and the code works as you would expect:
@ -839,7 +839,7 @@ export default function App() {
<label>
<input type="checkbox"
checked={canMove}
onChange={e => setCanMove(e.target.checked)}
onChange={e => setCanMove(e.target.checked)}
/>
The dot is allowed to move
</label>
@ -1094,7 +1094,7 @@ Now, when `increment` changes, React will re-synchronize your Effect, which will
#### Fix a freezing counter {/*fix-a-freezing-counter*/}
This `Timer` component keeps a `count` state variable which increases every second. The value by which it's increasing is stored in the `increment` state variable, which you can control it with the plus and minus buttons. For example, try pressing the plus button nine times, and notice that the `count` now increases by ten (rather than by one) after every next second.
This `Timer` component keeps a `count` state variable which increases every second. The value by which it's increasing is stored in the `increment` state variable, which you can control it with the plus and minus buttons. For example, try pressing the plus button nine times, and notice that the `count` now increases each second by ten rather than by one.
There is a small issue with this user interface. You might notice that if you keep pressing the plus or minus buttons faster than once per second, the timer itself seems to pause. It only resumes after a second passes since the last time you've pressed either button. Find why this is happening, and fix the issue so that the timer ticks on *every* second without interruptions.
@ -1523,7 +1523,7 @@ export default function App() {
<hr />
<ChatRoom
roomId={roomId}
theme={isDark ? 'dark' : 'light'}
theme={isDark ? 'dark' : 'light'}
/>
</>
);
@ -1683,7 +1683,7 @@ export default function App() {
<hr />
<ChatRoom
roomId={roomId}
theme={isDark ? 'dark' : 'light'}
theme={isDark ? 'dark' : 'light'}
/>
</>
);
@ -1845,7 +1845,7 @@ export default function App() {
<hr />
<ChatRoom
roomId={roomId}
theme={isDark ? 'dark' : 'light'}
theme={isDark ? 'dark' : 'light'}
/>
</>
);

13
beta/src/content/learn/you-might-not-need-an-effect.md

@ -177,7 +177,7 @@ Instead, you can tell React that each user's profile is conceptually a _differen
export default function ProfilePage({ userId }) {
return (
<Profile
userId={userId}
userId={userId}
key={userId}
/>
);
@ -284,7 +284,7 @@ function ProductPage({ product, addToCart }) {
// ✅ Good: Event-specific logic is called from event handlers
function buyProduct() {
addToCart(product);
showNotification(`Added ${product.name} to the shopping cart!`);
showNotification(`Added ${product.name} to the shopping cart!`);
}
function handleBuyClick() {
@ -448,7 +448,7 @@ This is a lot more efficient. Also, if you implement a way to view game history,
Remember that inside event handlers, [state behaves like a snapshot.](/learn/state-as-a-snapshot) For example, even after you call `setRound(round + 1)`, the `round` variable will reflect the value at the time the user clicked the button. If you need to use the next value for calculations, define it manually like `const nextRound = round + 1`.
In some cases, you *can't* calculate the next state directly in the event handler. For example, imagine a form with multiple dropdowns where the options of each next dropdown depend on the selected value of the previous dropdown. Then, a chain of Effects fetching data is appropriate because you are synchronizing with network.
In some cases, you *can't* calculate the next state directly in the event handler. For example, imagine a form with multiple dropdowns where the options of the next dropdown depend on the selected value of the previous dropdown. Then, a chain of Effects fetching data is appropriate because you are synchronizing with network.
### Initializing the application {/*initializing-the-application*/}
@ -719,7 +719,7 @@ However, the code above has a bug. Imagine you type `"hello"` fast. Then the `qu
```js {5,7,9,11-13}
function SearchResults({ query }) {
const [results, setResults] = useState([]);
const [page, setPage] = useState(1);
const [page, setPage] = useState(1);
useEffect(() => {
let ignore = false;
fetchResults(query, page).then(json => {
@ -747,7 +747,7 @@ If you don't use a framework (and don't want to build your own) but would like t
```js {4}
function SearchResults({ query }) {
const [page, setPage] = useState(1);
const [page, setPage] = useState(1);
const params = new URLSearchParams({ query, page });
const results = useData(`/api/search?${params}`);
@ -1518,7 +1518,7 @@ export default function EditContact(props) {
return (
<EditForm
{...props}
key={props.savedContact.id}
key={props.savedContact.id}
/>
);
}
@ -1726,4 +1726,3 @@ Notice how in this version, only _submitting the form_ (which is an event) cause
</Solution>
</Challenges>

Loading…
Cancel
Save