多步驟頁面的狀態管理 (React Hook - useContext)

很常見的一個應用場景
就是讓用戶完成多步驟的表單頁面並在最後送出
中間的過程中用戶可以自由返回上一頁
並對尚未提交的內容進行修改

之前有寫一篇前端路由的導航狀態
是講前端如何在不同的頁面之間傳遞數據的方法
雖然可以實現這個功能
但相當不方便
因為需要在每一頁重複設定一樣的方法將數據帶來帶去
這種情況如果將狀態統一管理就會輕鬆很多

狀態統一管理最常聽到的就是 Redux
然而在 React 裡頭還有 Context 這個方法
如果要說這兩個的差別在哪裡
我想 Context 更符合 React 的元件思維
也特別適合簡單的應用場景

所以這篇主要講 React 的 useContext:

  • 用 createContext 方法建立 Provider 元件
  • 在前端路由的 Layout 置放 Provider 元件
  • 在需要使用共同狀態的步驟頁面使用 useContext

用 createContext 方法建立 Provider 元件

createContext 創建出來的 Context.Provider 是元件容器的概念
裡面的設定的 value 是可以和底下元件共享的

另外因為是要做頁面級別的元件容器
所以還需要使用 react-router-dom 裡面的 Outlet

註:在 context 中用 setState 方法時所有子元件也會重新渲染,
如果應用場景較為複雜可加上 useReducer, 來更好的控制子元件更新以節省效能

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
32
33
34
35
36
37
38
39
40
import { createContext, useState, useEffect, useRef } from "react";
import { Outlet, useLocation } from "react-router-dom";

export const StepsContext = createContext();

const StepsProvider = () => {
const isMounted = useRef(false);
const location = useLocation();
const [info, setInfo] = useState({});

const stepsInit = () => {
setInfo({});
};

useEffect(() => {
if (!isMounted.current) {
isMounted.current = true;
} else {
// 如果有頁面需要將數據初始化可以這邊設定
if (["/path1", "/path2"].includes(location.pathname)) {
stepsInit();
}
}
}
}, [location, info]);

return (
<StepsContext.Provider
value={{
info,
setInfo,
stepsInit,
}}
>
<Outlet />
</StepsContext.Provider>
);
};

export default StepsProvider;

在前端路由的 Layout 置放 Provider 元件

這邊挺單純的
直接看代碼就可以了

1
2
3
4
5
6
7
8
9
10
<Routes>
<Route path="multiple-steps" element={<StepProvider />}>
<Route index element={<multipleStep />} />
<Route path="step1" element={<Step1 />} />
<Route path="step2" element={<Step2 />} />
<Route path="step3" element={<Step3 />} />
<Route path="step4" element={<Step4 />} />
<Route path="step5" element={<Step5 />} />
</Route>
</Routes>

在需要使用共同狀態的步驟頁面使用 useContext

這邊有個小技巧
可以將 setState 的觸發時機放在 componentWillUnmount 裡面
因為不論用戶按上一頁或下一頁都會觸發元件卸載
可以藉此來更新狀態!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const Step3 = () => {
const { info, setInfo } = useContext(StepsContext);

const {
register,
formState: { erros },
handleSubmit,
} = useForm({ defaultValues: info });

useEffect(() => {
return () => {
handleSubmit((data) => {
setInfo(data);
})();
};
}, [handleSubmit, setInfo]);

return (
// JSX 元件代碼
);
};

export default Step3;

本文作者: David Huang
本文地址https://davidblog.github.io/2023/07/27/多步驟頁面的狀態管理/

0%