隔了一天才搞清楚。
簡述
當時的情境是我想在 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([]); setIsLoading(true); useEffect(() => { getAllPosts().then((data) => { setPosts(data); setIsLoading(false); }); }, [setIsLoading]); }
|
接著就噴錯誤了:
Warning 的大意是說我不應該在 render 子元件的時候去更新 state。我一開始沒有很懂這什麼意思,不過後來仔細想一下流程後就明白了。
總之這是因為我在 render 子元件的時候去更新了 state,這時候就會觸發 App 重新 render,然後 render 子元件的時候又再更新了 state,觸發 App 重新 render …
大概就跟這張圖的意思一樣:
所以要避開這種:
- 在 render 時更新 state 的行為
- 在 render 時更新 state 的行為
- 在 render 時更新 state 的行為
這個真的很重要QQ,請務必搞清楚。
最後呢,這個範例的正確做法應該是這樣:
1 2 3 4 5 6 7 8
| useEffect(() => { setIsLoading(true); getAllPosts().then((data) => { setPosts(data); setIsLoading(false); }); }, [setIsLoading]);
|
希望以後別再踩到同樣的雷。