React配置Redux并结合本地存储设置token方式

 更新时间:2024年01月25日 09:38:47   作者:sonicwater  
这篇文章主要介绍了React配置Redux并结合本地存储设置token方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

React 项目使用 TypeScriptHooks API,本文介绍配置 Redux 并结合本地存储设置 token

安装依赖

yarn add redux -S
yarn add react-redux -S
  • redux 可以脱离 react 使用, react-redux 的作用主要是提供 <Provider> 标签包裹页面组件。

store 目录,以加减运算为例

src
 ├── store
 │     ├── actions
 │     │    └── counter.ts
 │     ├── reducers
 │     │    ├── counter.ts
 │     │    └── index.ts
 │     └── index.ts

./src/store/index.ts

import { createStore } from 'redux';
import allReducers from './reducers';
// 注册
const store = createStore(
  allReducers,
  // @ts-ignore
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() // 引入Redux调试工具
);
// 导出
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 有关的代码。
  • <Provider> 放在最外层,里面放路由组件。

用一个组件页面 CounterComponent 测试 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 两个 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 相关方法的引入。
const num = useSelector(state => (state as any).counter);
  • useSelector 可以访问并返回全部 store 中的子模块,这里只返回 counter 子模块。

可以参照上面例子写一个保存登录 login_token 的子模块,并结合 localStorage 根据登录状态控制页面跳转。

至于已经有 redux 为什么还要结合 localStorage ,这样的疑问,有两点原因:

  • redux 在页面刷新后值会被初始化,无法实现数据持久化。但是 redux 的数据可以影响子路由页面响应式变化。
  • localStorage 保存的数据不会被刷新等操作影响,可以持久化。但是 localStorage 不具备 redux 的响应式变化功能。

redux 中创建用户模块 user 里面保存 login_token

注意: 这里的 login_token 是调登录接口返回的经过加密的 32 位字符串,不是 JWT 标准格式的 token

修改一下目录,增加 user 相关文件。

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;
  • 所有对 login_token 的设置、获取、删除都先对本地存储进行响应操作,然后返回值。

修改 ./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;

页面相关操作

登录:

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) => {
    // 调用登陆Api,获取结果
    let res = await doLogin({...login});
	dispatch(setToken(res.data.login_token));
	// 跳转到 home 页面
	history.push('/home');
  }
  ...
}
  • 在验证登录信息后,调用登录接口,接口返回 login_token
  • dispatch(setToken(res.data.login_token)) 方法存储到 redux 中并页面跳转。

登出的逻辑:

  ...
    dispatch(deleteToken());
    history.push('/login');
  ...

useDispatch 属于 Hooks API ,它只能被用在函数式组件中。

如果要在一些配置文件如 API 接口的配置文件中使用,需要换一种写法。

...
import store from "src/store";
// axios实例拦截请求
axios.interceptors.request.use(
  (config: AxiosRequestConfig) => {
    ...
    Object.assign(config['post'], {
      login_token: store.getState().user
    });
    ...
    return config;
  },
  (error:any) => {
    return Promise.reject(error);
  }
)
...
  • 在调接口前拦截请求,在请求参数中添加 login_token
  • 注意写法:store.getState() 后面接的是模块名

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • useCallback和useMemo的正确用法详解

    useCallback和useMemo的正确用法详解

    这篇文章主要为大家介绍了useCallback和useMemo的正确用法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • React开启代理的2种实用方式

    React开启代理的2种实用方式

    最近有不少伙伴询问react的代理配置,自己也去试验了一下发现不少的问题,在这就将所遇到的心得分享出来,这篇文章主要给大家介绍了关于React开启代理的2种实用方式的相关资料,需要的朋友可以参考下
    2021-07-07
  • react自动化构建路由的实现

    react自动化构建路由的实现

    这篇文章主要介绍了react自动化构建路由的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • 浅谈React Native Flexbox布局(小结)

    浅谈React Native Flexbox布局(小结)

    这篇文章主要介绍了浅谈React Native Flexbox布局(小结),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • React点击事件的两种写法小结

    React点击事件的两种写法小结

    这篇文章主要介绍了React点击事件的两种写法小结,具有很好的参考价值,希望对大家有所帮助。
    2022-12-12
  • react native实现往服务器上传网络图片的实例

    react native实现往服务器上传网络图片的实例

    下面小编就为大家带来一篇react native实现往服务器上传网络图片的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • React 高阶组件与Render Props优缺点详解

    React 高阶组件与Render Props优缺点详解

    这篇文章主要weidajai 介绍了React 高阶组件与Render Props优缺点详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • 在react中使用highlight.js将页面上的代码高亮的方法

    在react中使用highlight.js将页面上的代码高亮的方法

    本文通过 highlight.js 库实现对文章正文 HTML 中的代码元素自动添加语法高亮,具有一定的参考价值,感兴趣的可以了解一下
    2022-01-01
  • ahooks封装cookie localStorage sessionStorage方法

    ahooks封装cookie localStorage sessionStorage方法

    这篇文章主要为大家介绍了ahooks封装cookie localStorage sessionStorage的方法示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • 通过示例讲解Remix 设计哲学理念

    通过示例讲解Remix 设计哲学理念

    这篇文章主要为大家通过示例讲解了Remix 设计哲学理念,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03

最新评论