使用 useContext 要注意的細節

隔了一天才搞清楚。

簡述

當時的情境是我想在 App 包裹一層 <Loading.Provider>,接著再透過子元件去呼叫 setIsLoading 來更新 state,像這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const [isLoading, setIsloading] = useState(false);

return (
<ThemeProvider theme={theme}>
<GlobalStyle />
{/* loading context */}
<Loading.Provider value={[isLoading, setIsloading]}>
<AuthContext.Provider value={{ user, setUser }}>
<Router>
<NavBar />
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/categories" element={<CategoriesPage />} />
<Route path="/posts" element={<PostsPage />} />
<Route path="/posts/:id" element={<SinglePostPage />} />
<Route path="/log-in" element={<LoginPage />} />
<Route path="/sign-up" element={<SignUpPage />} />
<Route path="/test" element={<TestPage />} />
</Routes>
{/* loading component */}
<StyledLoader
active={isLoading}
spinner={true}
text="Loading your content..."
></StyledLoader>
</Router>
</AuthContext.Provider>
</Loading.Provider>
</ThemeProvider>
)

接著我在子元件裡面這樣寫:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export default function HomePage() {

const [isLoading, setIsLoading] =useContext(Loading)
const [posts, setPosts] = useState([]);

// 一進來就打開 loading 畫面
setIsLoading(true);

useEffect(() => {
getAllPosts().then((data) => {
setPosts(data);
// 拿到 API 資料後再關閉
setIsLoading(false);
});
}, [setIsLoading]);

// ... 略
}

接著就噴錯誤了:

error

Warning 的大意是說我不應該在 render 子元件的時候去更新 state。我一開始沒有很懂這什麼意思,不過後來仔細想一下流程後就明白了。

總之這是因為我在 render 子元件的時候去更新了 state,這時候就會觸發 App 重新 render,然後 render 子元件的時候又再更新了 state,觸發 App 重新 render …

大概就跟這張圖的意思一樣:

loop

所以要避開這種:

  • 在 render 時更新 state 的行為
  • 在 render 時更新 state 的行為
  • 在 render 時更新 state 的行為

這個真的很重要QQ,請務必搞清楚。

最後呢,這個範例的正確做法應該是這樣:

1
2
3
4
5
6
7
8
useEffect(() => {
// 當 render 完以後才執行。
setIsLoading(true);
getAllPosts().then((data) => {
setPosts(data);
setIsLoading(false);
});
}, [setIsLoading]); // 只有在 setIsLoading 改變時,這個 Effect 才會再次執行

希望以後別再踩到同樣的雷。

React 基本的路由結構 mentor-program-day118
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×