状态更改后无法阻止 React 更新主要组件
Can't stop React from updating the main component after state change
我遇到了以下问题。
我有一个名为“BackgroundService”的组件,它有一个 setInterval,用于每 5 秒从 API 请求数据。从 API 接收到的数据存储在带有 useState 的“backgroundServiceResult”挂钩中,位于 App 中并由上下文提供程序共享。
_app.js:
const App = ({ Component, pageProps }) => {
const [backgroundServiceResult, setBackgroundServiceResult] = useState([false]);
console.log("App reloaded")
return (
<AppContext.Provider value={{ backgroundServiceResult, setBackgroundServiceResult }}>
<BackgroundService/>
<Component {...pageProps} />
</AppContext.Provider>
)
}
BackgroundService.js:
import { useState, useEffect, useContext } from "react"
import AppContext from '@/hooks/AppContext'
export const BackgroundService = () => {
const { getLatestSyncInfo } = api()
const { isDBSet, getJson } = OfflineStorage()
const appContext = useContext(AppContext);
const [alreadyNotified, setalreadyNotified] = useState(false)
useEffect(async () => {
const intervalId = setInterval(async () => {
// REQUIRE DATA FROM API STUFF, AND CAll:
appContext.setBackgroundServiceResult(data or stuff);
}, 5000)
return () => clearInterval(intervalId);
}, [])
return (
<></>
)
}
问题是,每次从BackgroundService.js调用appContext.setBackgroundServiceResult时,整个App组件都会重新渲染!于是调用App中的“console log”,重新挂载所有组件
如何通过我的所有应用程序存储从 API 接收到的数据,而无需再次从应用程序中全部呈现?
有什么办法解决这个问题吗?
谢谢
您的应用程序遵循预期的行为,当 state 或 props 更新时,组件将 re-render。
您可以使用许多选项来防止这种情况对您的应用程序的某些部分产生负面影响。
useEffect
只能用于 运行 子组件中的代码,当组件最初安装时或特定 props
或 state
更改时。
useMemo
可用于仅在特定 props
或 state
更改时重新计算值。
useCallback
可用于仅在特定 props
或 state
更改时重新创建函数。
在您的具体情况下,如果 BackgroundService
不会呈现任何内容,则创建它没有任何意义。相反,你应该像这样创建一个钩子:
import { useState, useEffect, useContext } from "react"
import AppContext from '@/hooks/AppContext'
export const useBackgroundService = () => {
const appContext = useContext(AppContext);
// Also bear in mind that the `useEffect` callback cannot be `async`
useEffect(() => {
// the `async` over here is fine though
const intervalId = setInterval(async () => {
appContext.setBackgroundServiceResult(data or stuff);
}, 5000)
return () => clearInterval(intervalId);
}, [])
}
然后在您的应用中调用它,如下所示:
const App = ({ Component, pageProps }) => {
const [backgroundServiceResult, setBackgroundServiceResult] = useState([false]);
useBackgroundService();
return (
<AppContext.Provider value={{ backgroundServiceResult, setBackgroundServiceResult }}>
<Component {...pageProps} />
</AppContext.Provider>
)
}
不用担心 console.log
失效,它不会对您的申请产生负面影响。如果您必须在应用程序组件的顶层对大量列表进行排序,您可以这样做:
const App = ({ Component, pageProps }) => {
const [backgroundServiceResult, setBackgroundServiceResult] = useState([false]);
useBackgroundService();
const sortedList = useMemo(() => pageProps.myList.sort(), [pageProps.myList]);
return (
<AppContext.Provider value={{ backgroundServiceResult, setBackgroundServiceResult }}>
<Component {...pageProps} />
</AppContext.Provider>
)
}
然后 sortedList
值只会在需要时更新,而您更新后的 backgroundServiceResult
不会导致重新计算该值。
以同样的方式,您可以在子组件中使用 useEffect
来确保代码仅在初始安装时 运行s 而不是在被 re-rendered 的组件上。
如果您更新您的问题以更具体地说明您的应用正在呈现的问题是什么,我们可以提出更好的解决方案来解决该特定问题。
我遇到了以下问题。
我有一个名为“BackgroundService”的组件,它有一个 setInterval,用于每 5 秒从 API 请求数据。从 API 接收到的数据存储在带有 useState 的“backgroundServiceResult”挂钩中,位于 App 中并由上下文提供程序共享。
_app.js:
const App = ({ Component, pageProps }) => {
const [backgroundServiceResult, setBackgroundServiceResult] = useState([false]);
console.log("App reloaded")
return (
<AppContext.Provider value={{ backgroundServiceResult, setBackgroundServiceResult }}>
<BackgroundService/>
<Component {...pageProps} />
</AppContext.Provider>
)
}
BackgroundService.js:
import { useState, useEffect, useContext } from "react"
import AppContext from '@/hooks/AppContext'
export const BackgroundService = () => {
const { getLatestSyncInfo } = api()
const { isDBSet, getJson } = OfflineStorage()
const appContext = useContext(AppContext);
const [alreadyNotified, setalreadyNotified] = useState(false)
useEffect(async () => {
const intervalId = setInterval(async () => {
// REQUIRE DATA FROM API STUFF, AND CAll:
appContext.setBackgroundServiceResult(data or stuff);
}, 5000)
return () => clearInterval(intervalId);
}, [])
return (
<></>
)
}
问题是,每次从BackgroundService.js调用appContext.setBackgroundServiceResult时,整个App组件都会重新渲染!于是调用App中的“console log”,重新挂载所有组件
如何通过我的所有应用程序存储从 API 接收到的数据,而无需再次从应用程序中全部呈现?
有什么办法解决这个问题吗?
谢谢
您的应用程序遵循预期的行为,当 state 或 props 更新时,组件将 re-render。
您可以使用许多选项来防止这种情况对您的应用程序的某些部分产生负面影响。
useEffect
只能用于 运行 子组件中的代码,当组件最初安装时或特定props
或state
更改时。useMemo
可用于仅在特定props
或state
更改时重新计算值。useCallback
可用于仅在特定props
或state
更改时重新创建函数。
在您的具体情况下,如果 BackgroundService
不会呈现任何内容,则创建它没有任何意义。相反,你应该像这样创建一个钩子:
import { useState, useEffect, useContext } from "react"
import AppContext from '@/hooks/AppContext'
export const useBackgroundService = () => {
const appContext = useContext(AppContext);
// Also bear in mind that the `useEffect` callback cannot be `async`
useEffect(() => {
// the `async` over here is fine though
const intervalId = setInterval(async () => {
appContext.setBackgroundServiceResult(data or stuff);
}, 5000)
return () => clearInterval(intervalId);
}, [])
}
然后在您的应用中调用它,如下所示:
const App = ({ Component, pageProps }) => {
const [backgroundServiceResult, setBackgroundServiceResult] = useState([false]);
useBackgroundService();
return (
<AppContext.Provider value={{ backgroundServiceResult, setBackgroundServiceResult }}>
<Component {...pageProps} />
</AppContext.Provider>
)
}
不用担心 console.log
失效,它不会对您的申请产生负面影响。如果您必须在应用程序组件的顶层对大量列表进行排序,您可以这样做:
const App = ({ Component, pageProps }) => {
const [backgroundServiceResult, setBackgroundServiceResult] = useState([false]);
useBackgroundService();
const sortedList = useMemo(() => pageProps.myList.sort(), [pageProps.myList]);
return (
<AppContext.Provider value={{ backgroundServiceResult, setBackgroundServiceResult }}>
<Component {...pageProps} />
</AppContext.Provider>
)
}
然后 sortedList
值只会在需要时更新,而您更新后的 backgroundServiceResult
不会导致重新计算该值。
以同样的方式,您可以在子组件中使用 useEffect
来确保代码仅在初始安装时 运行s 而不是在被 re-rendered 的组件上。
如果您更新您的问题以更具体地说明您的应用正在呈现的问题是什么,我们可以提出更好的解决方案来解决该特定问题。