From 55a265c8abc36ec1e781dd244adc8967789289cf Mon Sep 17 00:00:00 2001 From: Kanika Date: Tue, 13 Jan 2026 18:21:47 +0530 Subject: [PATCH 1/2] Clarify when Effects are needed for data fetching Improve the explanation in the 'Fetching data' section to clearly distinguish when an Effect is needed (synchronization with external system) versus when it's not needed (user events, data transformation). Closes #8226 --- src/content/learn/you-might-not-need-an-effect.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/content/learn/you-might-not-need-an-effect.md b/src/content/learn/you-might-not-need-an-effect.md index 81a0842eb60..4008f24c8cf 100644 --- a/src/content/learn/you-might-not-need-an-effect.md +++ b/src/content/learn/you-might-not-need-an-effect.md @@ -720,11 +720,11 @@ function SearchResults({ query }) { } ``` -You *don't* need to move this fetch to an event handler. +This is a case where an Effect *is* needed because you're [synchronizing](/learn/synchronizing-with-effects) with an external system (the network). However, this is different from the earlier examples where Effects weren't needed. -This might seem like a contradiction with the earlier examples where you needed to put the logic into the event handlers! However, consider that it's not *the typing event* that's the main reason to fetch. Search inputs are often prepopulated from the URL, and the user might navigate Back and Forward without touching the input. +The key difference: here, you need to fetch data whenever `query` or `page` changes, regardless of *how* they changed. The `query` might come from the URL (when the user navigates Back/Forward), or it might be typed by the user. The `page` might change from a button click or from restoring scroll position. It doesn't matter where `page` and `query` come from—while this component is visible, you want to keep `results` synchronized with data from the network for the current `page` and `query`. This is why it's an Effect. -It doesn't matter where `page` and `query` come from. While this component is visible, you want to keep `results` [synchronized](/learn/synchronizing-with-effects) with data from the network for the current `page` and `query`. This is why it's an Effect. +In contrast, if fetching was only needed in response to a specific user action (like clicking a "Search" button), you would put it in that event handler instead. However, the code above has a bug. Imagine you type `"hello"` fast. Then the `query` will change from `"h"`, to `"he"`, `"hel"`, `"hell"`, and `"hello"`. This will kick off separate fetches, but there is no guarantee about which order the responses will arrive in. For example, the `"hell"` response may arrive *after* the `"hello"` response. Since it will call `setResults()` last, you will be displaying the wrong search results. This is called a ["race condition"](https://en.wikipedia.org/wiki/Race_condition): two different requests "raced" against each other and came in a different order than you expected. From 0d1b20186b3c2c0ec49a6182640509af397d2fd6 Mon Sep 17 00:00:00 2001 From: Kanika Date: Fri, 16 Jan 2026 19:27:18 +0530 Subject: [PATCH 2/2] Fix content jumping on Opera mobile browsers Add Opera browser detection to scroll restoration logic to fix content jumping issue when scrolling on Opera on iPhone. Opera has similar scroll behavior to Safari and needs the same scroll restoration fix. This sets history.scrollRestoration to 'auto' for Opera browsers, preventing the content jumping issue. Fixes #7777 --- src/pages/_app.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 80a0a0f8641..b11000f08f6 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -35,11 +35,14 @@ export default function MyApp({Component, pageProps}: AppProps) { useEffect(() => { // Taken from StackOverflow. Trying to detect both Safari desktop and mobile. const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); - if (isSafari) { + // Detect Opera (desktop uses OPR, mobile may use Opera in user agent) + const isOpera = /OPR|Opera/i.test(navigator.userAgent); + if (isSafari || isOpera) { // This is kind of a lie. // We still rely on the manual Next.js scrollRestoration logic. - // However, we *also* don't want Safari grey screen during the back swipe gesture. + // However, we *also* don't want Safari/Opera grey screen during the back swipe gesture. // Seems like it doesn't hurt to enable auto restore *and* Next.js logic at the same time. + // Opera on mobile has similar scroll behavior issues as Safari, so it needs the same fix. history.scrollRestoration = 'auto'; } else { // For other browsers, let Next.js set scrollRestoration to 'manual'.