You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/content/learn/synchronizing-with-effects.md
+12-12Lines changed: 12 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -47,7 +47,7 @@ To write an Effect, follow these three steps:
47
47
48
48
1.**Declare an Effect.** By default, your Effect will run after every [commit](/learn/render-and-commit).
49
49
2.**Specify the Effect dependencies.** Most Effects should only re-run *when needed* rather than after every render. For example, a fade-in animation should only trigger when a component appears. Connecting and disconnecting to a chat room should only happen when the component appears and disappears, or when the chat room changes. You will learn how to control this by specifying *dependencies.*
50
-
3.**Add cleanup if needed.** Some Effects need to specify how to stop, undo, or clean up whatever they were doing. For example, "connect" needs "disconnect", "subscribe" needs "unsubscribe", and "fetch" needs either "cancel"or "ignore". You will learn how to do this by returning a *cleanup function*.
50
+
3.**Add cleanup if needed.** Some Effects need to specify how to stop, undo, or clean up whatever they were doing. For example, "connect" needs "disconnect", "subscribe" needs "unsubscribe", and "fetch" needs "cancel", "ignore", or an "isCurrent" check. You will learn how to do this by returning a *cleanup function*.
51
51
52
52
Let's look at each of these steps in detail.
53
53
@@ -684,30 +684,30 @@ In development, opacity will be set to `1`, then to `0`, and then to `1` again.
684
684
685
685
### Fetching data {/*fetching-data*/}
686
686
687
-
If your Effect fetches something, the cleanup function should either [abort the fetch](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) or ignore its result:
687
+
If your Effect fetches something, the cleanup function should either [abort the fetch](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) or ignore its result if it is not the most current rendering:
688
688
689
689
```js {2,6,13-15}
690
690
useEffect(() => {
691
-
letignore=false;
691
+
letisCurrent=true;
692
692
693
693
asyncfunctionstartFetching() {
694
694
constjson=awaitfetchTodos(userId);
695
-
if (!ignore) {
695
+
if (isCurrent) {
696
696
setTodos(json);
697
697
}
698
698
}
699
699
700
700
startFetching();
701
701
702
702
return () => {
703
-
ignore=true;
703
+
isCurrent=false;
704
704
};
705
705
}, [userId]);
706
706
```
707
707
708
708
You can't "undo" a network request that already happened, but your cleanup function should ensure that the fetch that's _not relevant anymore_ does not keep affecting your application. If the `userId` changes from `'Alice'` to `'Bob'`, cleanup ensures that the `'Alice'` response is ignored even if it arrives after `'Bob'`.
709
709
710
-
**In development, you will see two fetches in the Network tab.** There is nothing wrong with that. With the approach above, the first Effect will immediately get cleaned up so its copy of the `ignore` variable will be set to `true`. So even though there is an extra request, it won't affect the state thanks to the `if (!ignore)` check.
710
+
**In development, you will see two fetches in the Network tab.** There is nothing wrong with that. With the approach above, the first Effect will immediately get cleaned up so its copy of the `isCurrent` variable will be set to `false`. So even though there is an extra request, it won't affect the state thanks to the `if (isCurrent)` check.
711
711
712
712
**In production, there will only be one request.** If the second request in development is bothering you, the best approach is to use a solution that deduplicates requests and caches their responses between components:
713
713
@@ -1551,15 +1551,15 @@ export default function Page() {
1551
1551
const [person, setPerson] =useState('Alice');
1552
1552
const [bio, setBio] =useState(null);
1553
1553
useEffect(() => {
1554
-
letignore=false;
1554
+
letisCurrent=true;
1555
1555
setBio(null);
1556
1556
fetchBio(person).then(result=> {
1557
-
if (!ignore) {
1557
+
if (isCurrent) {
1558
1558
setBio(result);
1559
1559
}
1560
1560
});
1561
1561
return () => {
1562
-
ignore=true;
1562
+
isCurrent=false;
1563
1563
}
1564
1564
}, [person]);
1565
1565
@@ -1593,16 +1593,16 @@ export async function fetchBio(person) {
1593
1593
1594
1594
</Sandpack>
1595
1595
1596
-
Each render's Effect has its own `ignore` variable. Initially, the `ignore` variable is set to `false`. However, if an Effect gets cleaned up (such as when you select a different person), its `ignore` variable becomes `true`. So now it doesn't matter in which order the requests complete. Only the last person's Effect will have `ignore` set to `false`, so it will call `setBio(result)`. Past Effects have been cleaned up, so the `if (!ignore)` check will prevent them from calling `setBio`:
1596
+
Each render's Effect has its own `isCurrent` variable. Initially, the `isCurrent` variable is set to `true`. However, if an Effect gets cleaned up (such as when you select a different person), its `isCurrent` variable becomes `false`. So now it doesn't matter in which order the requests complete. Only the last person's Effect will have `isCurrent` set to `true`, so it will call `setBio(result)`. Past Effects have been cleaned up, so the `if (isCurrent)` check will prevent them from calling `setBio`:
1597
1597
1598
1598
- Selecting `'Bob'` triggers `fetchBio('Bob')`
1599
1599
- Selecting `'Taylor'` triggers `fetchBio('Taylor')` **and cleans up the previous (Bob's) Effect**
- The Effect from the `'Taylor'` render calls `setBio('This is Taylor’s bio')`
1602
1602
- Fetching `'Bob'` completes
1603
-
- The Effect from the `'Bob'` render **does not do anything because its `ignore` flag was set to `true`**
1603
+
- The Effect from the `'Bob'` render **does not do anything because its `isCurrent` flag was set to `false`**
1604
1604
1605
-
In addition to ignoring the result of an outdated API call, you can also use [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) to cancel the requests that are no longer needed. However, by itself this is not enough to protect against race conditions. More asynchronous steps could be chained after the fetch, so using an explicit flag like `ignore` is the most reliable way to fix this type of problem.
1605
+
In addition to ignoring the result of an outdated API call, you can also use [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) to cancel the requests that are no longer needed. However, by itself this is not enough to protect against race conditions. More asynchronous steps could be chained after the fetch, so using an explicit flag like `isCurrent` is the most reliable way to fix this type of problem.
0 commit comments