在react裡,元件間的資料主要是靠props來傳遞,然而,當元件越來越多的時候,這樣的傳遞就會過於複雜,這時候就可以利用context來分享資料。在Firebase Advanced Topics裡,我們介紹了ThemeProvider,其實,ThemeProvider就是利用context。當然,也可以使用更複雜的Redux/Recoil來達成這樣的效果,不過,多數的情況下,Context就能解決問題了。
假如我們想把登入狀況的資料放在Context裡,要怎麼處理?
首先,先新增一個AuthContext.js,在AuthContext.js裡,先利用createContext,產生一個context物件。
/src/account/AuthContext.js
import React from 'react';
/*
status及setStatus在provider會被覆蓋
status為toSignIn 已註冊,將要登入
status為toSignOut 已登入,將要登出
status為toSignUp 未註冊,將要註冊
*/
export const STATUS = {
toSignIn: 1,
toSignOut: 2,
toSignUp: 0,
};
export const AuthContext = React.createContext({
status: STATUS.toSignIn
})
就像使用Theme一樣,我們想要讓所有元件都可以使用到AuthContext的內容,所以,我們加上Provider,並提供預設的值。
/src/App.js
import "./styles.css";
import { BrowserRouter as Router } from "react-router-dom";
//import { HashRouter as Router } from "react-router-dom";
import { Routes, Route } from "react-router-dom";
import { createTheme, ThemeProvider } from "@mui/material/styles";
import { green, purple } from "@mui/material/colors";
import Exam from "./ExamEnglish";
import Chemistry from "./Chemistry";
import Image from "./Image";
import Main from "./ui/Main";
**import { AuthContext, STATUS } from "./account/AuthContext";**
const theme = createTheme({
palette: {
primary: {
main: purple[500]
},
secondary: {
main: green[500]
}
}
});
export default function App() {
return (
**<AuthContext.Provider value={{ status: STATUS.toSignIn }}>**
<ThemeProvider theme={theme}>
<Router>
<Routes>
<Route path="/" element={<Main />} />
<Route path="/exam" element={<Exam />} />
<Route path="/chemistry" element={<Chemistry />} />
<Route path="/image" element={<Image />} />
</Routes>
</Router>
</ThemeProvider>
**</AuthContext.Provider>**
);
}
類似Theme的使用,透過useContext()取得status的值。
/src/ui/AppMenu.js
import React, **{ useContext }** from "react";
import { NavLink } from "react-router-dom";
import { AppBar, Button, Toolbar } from "@mui/material";
import { useTheme } from "@mui/material/styles";
**import { AuthContext, STATUS } from "/src/account/AuthContext";**
export default function AppMenu() {
const theme = useTheme();
**const authContext = useContext(AuthContext);**
const activeStyle = {
backgroundColor: theme.palette.secondary.main,
color: "black"
};
const inActiveStyle = { backgroundColor: "inherit", color: "inherit" };
return (
<AppBar position="sticky">
<Toolbar>
<Button
component={NavLink}
to="/"
style={({ isActive }) => (isActive ? activeStyle : inActiveStyle)}
>
Main
</Button>
<Button
component={NavLink}
to="/exam"
style={({ isActive }) => (isActive ? activeStyle : inActiveStyle)}
>
Exam
</Button>
<Button
component={NavLink}
to="/chemistry"
style={({ isActive }) => (isActive ? activeStyle : inActiveStyle)}
>
Chemistry
</Button>
<Button
component={NavLink}
to="/image"
style={({ isActive }) => (isActive ? activeStyle : inActiveStyle)}
>
Image
</Button>
**{authContext.status === STATUS.toSignIn ? "未登入" : "已登入"}**
</Toolbar>
</AppBar>
);
}
在介紹Theme的時候,並沒有介紹如何去更動內容,在這裡,因為登入之後狀態必須被更新,要怎麼處理呢?
先在AuthContext留一個空的方法,其實這裡的變數值及方法都會在設定Provider時覆蓋,在這裡寫的好處是可以知道AuthContext會有status變數及set方法。
/src/account/AuthContext.js
import React from "react";
/*
status及setStatus在provider會被覆蓋
status為toSignIn 已註冊,將要登入
status為toSignOut 已登入,將要登出
status為toSignUp 未註冊,將要註冊
*/
export const STATUS = {
toSignIn: 1,
toSignOut: 2,
toSignUp: 0
};
export const AuthContext = React.createContext({
status: STATUS.toSignIn,
**setStatus: (newStatus) => {
this.status = newStatus;
}**
});