React 中 useContext 跟 React-MobX 的差異

2024年9月26日5 分鐘閱讀

React 中 useContext 跟 React-MobX 的差異

useContext

useContext 是 react 提供的一個 hook,可以讓我們透過讀取和訂閱元件中 context 共享元件之間的資料,主要是用來解決狀態提升和 prop drilling 的問題(即層層傳遞 props)。

1const AuthContext = createContext(null);
2
3export default function App() {
4 const [currentUser, setCurrentUser] = useState("");
5 console.log("App rendered");
6 return (
7 <AuthContext.Provider value={{ currentUser, setCurrentUser }}>
8 <Form />
9 </AuthContext.Provider>
10 );
11}
12
13function WelcomeCard() {
14 const { currentUser, setCurrentUser } = useContext(AuthContext);
15 console.log("WelcomeCard rendered");
16 return (
17 <div>
18 <h1>Welcome, {currentUser}!</h1>
19 <button onClick={() => setCurrentUser("")}>Log out</button>
20 </div>
21 );
22}
23
24function News() {
25 console.log("News rendered");
26 return (
27 <div>
28 <h1>Latest News</h1>
29 <ul>
30 <li>ewrweorjewoirj</li>
31 <li>ewwwwwrj</li>
32 <li>wjwirjwi</li>
33 </ul>
34 </div>
35 );
36}
37
38function Form() {
39 const { currentUser, setCurrentUser } = useContext(AuthContext);
40 const [name, setName] = useState("");
41
42 function handleSubmit(event) {
43 event.preventDefault();
44 setCurrentUser(name);
45 }
46 console.log("Form rendered");
47 return (
48 <section>
49 {currentUser && <WelcomeCard />}
50 {!currentUser && (
51 <form onSubmit={handleSubmit}>
52 <div>
53 <label name="username">
54 username
55 <input
56 type={"text"}
57 id="username"
58 onChange={(event) => setName(event.target.value)}
59 />
60 </label>
61 </div>
62 <button type="submit">Log in</button>
63 </form>
64 )}
65 <News />
66 </section>
67 );
68}
CodeSandbox 展示

在這例子中,使用 useContext 實作了一個簡單的登入表單,當使用者輸入名稱後,點擊登入按鈕,就會顯示歡迎卡片,並且可以登出。

在 App 元件中,使用 AuthContext.Provider 提供了 currentUsersetCurrentUser 兩個方法,透過 useContext 可以在子元件中取得這兩個方法。

子元件透過 setCurrentUser 方法,可以改變 AuthContextcurrentUser 的值,並且重新 render 子元件。

透過 Context 傳遞資料

WelcomeCard 中,我們透過 useContext 取得 currentUsersetCurrentUser,不需要透過 props 傳遞資料,就可以取得 currentUser 的值。

更新 Context 的值

在 App 元件中,<AuthContext.Provider value={{ currentUser, setCurrentUser }}></<AuthContext.Provider>向下子元件提供 AuthContext 的初始值 currentUser 與更新方法 setCurrentUser 。

Form 元件中,我們透過const { currentUser, setCurrentUser } = useContext(AuthContext); 去取得 currentUsersetCurrentUser,並且在 handleSubmit 方法中,透過 setCurrentUser(name) 去更新 AuthContextcurrentUser 的值。

Context 的值改變時,會 re-render 子元件

接續上方,當提交表單後,currentUser 的值改變,AuthContextcurrentUser 的值也會改變,這時候從 AuthContext 所在的元件 App 向下開始 re-render,並且接續 re-render 子元件 FormWelcomeCardNews

React-MobX

React-MobX 是一個 React 應用程式狀態管理的工具,透過觀察者模式,當 MobX 狀態變更時元件可以自動更新 UI,不需要手動去更新元件。

1const Board = observer(() => {
2 console.log("Board rendered");
3 return <h1>Count: {counterStore.count}</h1>;
4});
5
6export default function App() {
7 console.log("App rendered");
8 return (
9 <div>
10 <h1>MobX-State-Tree 計數器範例</h1>
11 <Board />
12 <Counter />
13 </div>
14 );
15}
1// 定義一個 Counter 模型
2const CounterModel = types
3 .model({
4 count: types.number, // 可觀察的狀態
5 })
6 .actions((self) => ({
7 // 定義 action 來修改狀態
8 increment() {
9 self.count += 1;
10 },
11 decrement() {
12 self.count -= 1;
13 },
14 }));
15
16// 創建模型實例
17const counterStore = CounterModel.create({
18 count: 0,
19});
20
21export default counterStore;
  • 使用了 mobx-state-tree 來定義一個結構化的狀態樹 CounterModel,其中有一個 count 狀態和兩個修改該狀態的 action (incrementdecrement)。

  • 使用 types.model 定義 MobX-State-Tree 模型,然後通過 actions 來定義修改狀態的方法。

1// observer 使 React 元件能觀察 MobX 的狀態變更
2// 引入剛才定義的 store
3
4const Counter = observer(() => {
5 console.log("Counter rendered");
6 return (
7 <div>
8 <button onClick={() => counterStore.increment()}>Increment</button>
9 <button onClick={() => counterStore.decrement()}>Decrement</button>
10 </div>
11 );
12});
13
14export default Counter;
  • 使用 observer 讓 React 元件能夠觀察並反應 MobX-State-Tree 狀態的變化。當 counterStore.count 改變時,React 元件會自動重新渲染。

  • 透過按鈕點擊來觸發 incrementdecrement action,這些方法會自動更新狀態。

  • 在這個例子中,只會重新渲染 Board 元件,不會像使用 useContext 一樣向下更新使用該 context的子元件。

    https://codesandbox.io/p/sandbox/5lkzr4

CodeSandbox 展示

useContext 和 React-MobX 的差異

特性useContext React-MobX
狀態管理範圍可以包裹在特定 component tree ,範圍靈活只能在全域最上層
狀態變更與更新機制context 的值更新時,所有使用該 context 的元件都會重新渲染,無論這些組件是否實際依賴被更新的值。僅重新渲染觀察到狀態變更的元件,精細控制渲染粒度
應用場景適合多語系、主題等較簡單的全域性的狀態管理適合較大型的應用程式,需要更細粒度的狀態管理

標籤:

React前端JavaScriptComponentStatePropsContextEvent