React配置Redux并結(jié)合本地存儲(chǔ)設(shè)置token方式
此 React 項(xiàng)目使用 TypeScript 和 Hooks API,本文介紹配置 Redux 并結(jié)合本地存儲(chǔ)設(shè)置 token
安裝依賴
yarn add redux -S yarn add react-redux -S
- redux 可以脫離 react 使用, react-redux 的作用主要是提供 <Provider> 標(biāo)簽包裹頁面組件。
store 目錄,以加減運(yùn)算為例
src ├── store │ ├── actions │ │ └── counter.ts │ ├── reducers │ │ ├── counter.ts │ │ └── index.ts │ └── index.ts
./src/store/index.ts
import { createStore } from 'redux'; import allReducers from './reducers'; // 注冊(cè) const store = createStore( allReducers, // @ts-ignore window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() // 引入Redux調(diào)試工具 ); // 導(dǎo)出 export default store;
./src/store/actions/counter.ts
export interface action { type: 'INCREMENT' | 'DECREMENT'; num?: number; } export const increment = (num: number = 1): action => ({ type: 'INCREMENT', num }); export const decrement = (num: number = 1): action => ({ type: 'DECREMENT', num });
./src/store/reducers/index.ts
import { combineReducers } from "redux"; import counter from "./counter"; // 整合 const allReducers = combineReducers({ counter }); export default allReducers;
./src/store/reducers/counter.ts
interface action { type: "INCREMENT" | "DECREMENT"; num?: number; } const counter = (state = 0, action: action) => { switch (action.type) { case "INCREMENT": return state + (action.num as number); case "DECREMENT": return state - (action.num as number); default: return state; } }; export default counter;
再看 ./src/app.tsx
import { FC } from 'react'; import { Provider } from 'react-redux'; import store from 'src/store'; ... const App: FC = () => { return ( <Provider store={store}> ... </Provider> ); }
- 只列出和 react-redux、store 有關(guān)的代碼。
- <Provider> 放在最外層,里面放路由組件。
用一個(gè)組件頁面 CounterComponent 測(cè)試 store 中的 counter 模塊。
import { FC } from 'react'; import { Button } from 'antd'; import { useDispatch, useSelector } from 'react-redux'; import { increment, decrement } from "src/store/actions/counter"; const CounterComponent: FC = () => { const dispatch = useDispatch(); const num = useSelector(state => (state as any).counter); return( <> <div className="text-blue-500"> { num } </div> <Button type="default" onClick={() => dispatch(decrement())}>-1</Button> <Button type="primary" onClick={() => dispatch(increment())}>+1</Button> </> ) } export default CounterComponent;
- 注意 react-redux 提供的 useDispatch、useSelector 兩個(gè) Hooks 的使用。
... return( <> <div className="text-blue-500"> { num } </div> <Button type="default" onClick={() => dispatch({ type: 'DECREMENT', num: 1 })}>-1</Button> <Button type="primary" onClick={() => dispatch({ type: 'INCREMENT', num: 1 })}>+1</Button> </> ) ...
- dispatch 也可以像上面這樣寫,如此可以省略 src/store/actions/counter 相關(guān)方法的引入。
const num = useSelector(state => (state as any).counter);
- useSelector 可以訪問并返回全部 store 中的子模塊,這里只返回 counter 子模塊。
可以參照上面例子寫一個(gè)保存登錄 login_token 的子模塊,并結(jié)合 localStorage 根據(jù)登錄狀態(tài)控制頁面跳轉(zhuǎn)。
至于已經(jīng)有 redux 為什么還要結(jié)合 localStorage ,這樣的疑問,有兩點(diǎn)原因:
- redux 在頁面刷新后值會(huì)被初始化,無法實(shí)現(xiàn)數(shù)據(jù)持久化。但是 redux 的數(shù)據(jù)可以影響子路由頁面響應(yīng)式變化。
- localStorage 保存的數(shù)據(jù)不會(huì)被刷新等操作影響,可以持久化。但是 localStorage 不具備 redux 的響應(yīng)式變化功能。
在 redux 中創(chuàng)建用戶模塊 user 里面保存 login_token。
注意: 這里的 login_token 是調(diào)登錄接口返回的經(jīng)過加密的 32 位字符串,不是 JWT 標(biāo)準(zhǔn)格式的 token
修改一下目錄,增加 user 相關(guān)文件。
src ├── store │ ├── actions │ │ ├── counter.ts │ │ └── user.ts │ ├── reducers │ │ ├── counter.ts │ │ ├── index.ts │ │ └── user.ts │ └── index.ts
./src/store/actions/user
export interface action { type: "SET_TOKEN" | "DELETE_TOKEN"; login_token?: string; } export const setToken = (login_token: string): action => ({ type: "SET_TOKEN", login_token }); export const deleteToken = (): action => ({ type: "DELETE_TOKEN" });
./src/store/reducers/user
interface action { type: "SET_TOKEN" | "DELETE_TOKEN"; token?: string; } const user = ( state='', action: action ) => { switch (action.type) { case "SET_TOKEN": state = action.token as string; localStorage.setItem('login_token', state); break case "DELETE_TOKEN": localStorage.removeItem('login_token'); state = ''; break default: state = localStorage.getItem('login_token') || ''; break } return state; }; export default user;
- 所有對(duì) login_token 的設(shè)置、獲取、刪除都先對(duì)本地存儲(chǔ)進(jìn)行響應(yīng)操作,然后返回值。
修改 ./src/store/reducers/index.ts
import { combineReducers } from "redux"; import counter from "./counter"; import user from "./user"; // 整合 const allReducers = combineReducers({ counter, user }); export default allReducers;
頁面相關(guān)操作
登錄:
import { useDispatch } from 'react-redux'; import { setToken } from "src/store/actions/user"; import { useHistory } from 'react-router-dom'; interface LoginEntity { username: string; password: string; } const Login = () => { const dispatch = useDispatch(); const history = useHistory(); ... // 登陸按鈕邏輯 const handleLogin = async (login:LoginEntity) => { // 調(diào)用登陸Api,獲取結(jié)果 let res = await doLogin({...login}); dispatch(setToken(res.data.login_token)); // 跳轉(zhuǎn)到 home 頁面 history.push('/home'); } ... }
- 在驗(yàn)證登錄信息后,調(diào)用登錄接口,接口返回 login_token
- dispatch(setToken(res.data.login_token)) 方法存儲(chǔ)到 redux 中并頁面跳轉(zhuǎn)。
登出的邏輯:
... dispatch(deleteToken()); history.push('/login'); ...
useDispatch 屬于 Hooks API ,它只能被用在函數(shù)式組件中。
如果要在一些配置文件如 API 接口的配置文件中使用,需要換一種寫法。
... import store from "src/store"; // axios實(shí)例攔截請(qǐng)求 axios.interceptors.request.use( (config: AxiosRequestConfig) => { ... Object.assign(config['post'], { login_token: store.getState().user }); ... return config; }, (error:any) => { return Promise.reject(error); } ) ...
- 在調(diào)接口前攔截請(qǐng)求,在請(qǐng)求參數(shù)中添加 login_token
- 注意寫法:store.getState() 后面接的是模塊名
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
React Router 5.1.0使用useHistory做頁面跳轉(zhuǎn)導(dǎo)航的實(shí)現(xiàn)
本文主要介紹了React Router 5.1.0使用useHistory做頁面跳轉(zhuǎn)導(dǎo)航的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11使用React?Hooks模擬生命周期的實(shí)現(xiàn)方法
這篇文章主要介紹了使用React?Hooks模擬生命周期,本文舉例說明如何使用 hooks 來模擬比較常見的 class 組件生命周期,需要的朋友可以參考下2023-02-02react-router?重新加回跳轉(zhuǎn)攔截功能詳解
這篇文章主要為大家介紹了react-router?重新加回跳轉(zhuǎn)攔截功能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02react使用antd的上傳組件實(shí)現(xiàn)文件表單一起提交功能(完整代碼)
最近在做一個(gè)后臺(tái)管理項(xiàng)目,涉及到react相關(guān)知識(shí),項(xiàng)目需求需要在表單中帶附件提交,怎么實(shí)現(xiàn)這個(gè)功能呢?下面小編給大家?guī)砹藃eact使用antd的上傳組件實(shí)現(xiàn)文件表單一起提交功能,一起看看吧2021-06-06使用 React Router Dom 實(shí)現(xiàn)路由導(dǎo)航的詳細(xì)過程
React Router Dom 是 React 應(yīng)用程序中用于處理路由的常用庫(kù),它提供了一系列組件和 API 來管理應(yīng)用程序的路由,這篇文章主要介紹了使用 React Router Dom 實(shí)現(xiàn)路由導(dǎo)航,需要的朋友可以參考下2024-03-03React倒計(jì)時(shí)功能實(shí)現(xiàn)代碼——解耦通用
這篇文章主要介紹了React倒計(jì)時(shí)功能實(shí)現(xiàn)——解耦通用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09React?Hook?Form?優(yōu)雅處理表單使用指南
這篇文章主要為大家介紹了React?Hook?Form?優(yōu)雅處理表單使用指南,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03