even

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

1、Redux Toolkit

Redux Toolkit是官方推荐的编写Redux的逻辑写法 简称RTK, 这工具是为了实现标准化逻辑,方便对redux进行简化管理

2、Redux Toolkit的安装

npm install @reduxjs/toolkit

// 注意:这个工具简化的是操作,集成了redux-thunk以及redux-devtools的配置,但是react-redux这个依赖还是需要安装

redux toolkit核心api

confiqureStore: 包装createStore以提供简化的配置选项和良好的默认值。它可以自动组合你的 slice reducer,添加你提供的任何 Redux中间件,redux-thunk默认包含,并启用 Redux DevTools Extension

createSlice: 接受reducer函数的对象、切片名称和初始状态值,并自动生成切片reducer,并带有相应的actions.

createAsyncThunk: 接受一个动作类型字符串和一个返回承诺的函数,并生成-个pending/fulfiled/rejected基于该承诺分派动作类型的 thunk

3、Redux Toolkit的使用

使用的例子是沿着一的例子进行调整

创建模块信息

import {
  createSlice,
  PayloadAction,
  SliceCaseReducers,
} from '@reduxjs/toolkit';

export interface ICounterState {
  counter: number;

  userInfo: { name: string; email: string };
}

export interface ICounterReducer extends SliceCaseReducers<ICounterState> {
  addNumberCounter: (
    state: ICounterState,
    action: PayloadAction<number>,
  ) => void;
  reduceNumberCounter: (
    state: ICounterState,
    action: PayloadAction<number>,
  ) => void;
}

const counter = createSlice<ICounterState, ICounterReducer>({
  name: 'counter',
  initialState: {
    counter: 0,
    userInfo: {
      name: '',
      email: '',
    },
  },
  reducers: {
    addNumberCounter: (state: ICounterState, action: PayloadAction<number>) => {
      state.counter += action.payload;
    },
    reduceNumberCounter: (
      state: ICounterState,
      action: PayloadAction<number>,
    ) => {
      state.counter -= action.payload;
    },
  },
});

// 导出action
export const { addNumberCounter, reduceNumberCounter } = counter.actions;
// 导出reducer
export default counter.reducer;

store的总的出口

import { configureStore } from '@reduxjs/toolkit';
import counterReducer, { ICounterState } from './modules/counter';

export interface IRootState {
  counter: ICounterState;
}

const store = configureStore({
  reducer: { counter: counterReducer },
});

export default store;

组件根节点的导入

import { Provider } from 'react-redux';
import { PureComponent, ReactElement } from 'react';

import store from './store';
import Content from './components/content';

class App extends PureComponent {
  public render(): ReactElement {
    return (
      <Provider store={store}>
        <h1>this is app</h1>
        <Content />
      </Provider>
    );
  }
}

export default App;

组件中使用store

import { connect } from 'react-redux';
import { PureComponent, ReactElement } from 'react';
import {
  addNumberCounter,
  reduceNumberCounter,
} from '../store/modules/counter';
import { IRootState } from '../store';
import { Dispatch } from '@reduxjs/toolkit';

interface IContentProps {
  counter: number;

  userInfo: { name: string; email: string };

  addCounter: (num: number) => void;

  reduceCounter: (num: number) => void;
}

class Content extends PureComponent<IContentProps> {
  public render(): ReactElement {
    const { counter, addCounter, reduceCounter, userInfo } = this.props;
    return (
      <div>
        <div>
          <span>counter: </span>
          <span>{counter}</span>
        </div>
        <div>
          <button onClick={() => addCounter(1)}>+1</button>
          <button onClick={() => reduceCounter(1)}>-1</button>
        </div>
        <div>
          <div>
            <span>user: </span>
            <span>{userInfo.name}</span>
          </div>
          <div>
            <span>email: </span>
            <span>{userInfo.email}</span>
          </div>
          <button>获取用户信息</button>
        </div>
      </div>
    );
  }
}

// 这个是把state注入到props
const mapStateToProps = (state: IRootState) => {
  return {
    counter: state.counter.counter,

    userInfo: state.counter.userInfo,
  };
};

// 这个是把方法注入的props
const mapDispatchToProps = (dispatch: Dispatch) => ({
  addCounter: (num: number) => dispatch(addNumberCounter(num)),
  reduceCounter: (num: number) => dispatch(reduceNumberCounter(num)),
});

export default connect(mapStateToProps, mapDispatchToProps)(Content);

4、Redux Toolkit中进行接口或者异步操作

 添加异步函数后的模块

import {
  createAsyncThunk,
  createSlice,
  PayloadAction,
  SliceCaseReducers,
  SerializedError,
} from '@reduxjs/toolkit';

const userInfoApi = (): Promise<{ name: string; email: string }> => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({ name: 'even', email: '*****@163.com' });
      // reject('this is error');
    }, 1000);
  });
};

// 这里需要导出,方便外面dispatch调用 export const fetchUserInfo
= createAsyncThunk( 'fetchUserInfo', () => userInfoApi(), // 如果是通过axios时,那么需要返回data
); export interface ICounterState { counter: number; userInfo: { name: string; email: string }; } export interface ICounterReducer extends SliceCaseReducers<ICounterState> { addNumberCounter: ( state: ICounterState, action: PayloadAction<number>, ) => void; reduceNumberCounter: ( state: ICounterState, action: PayloadAction<number>, ) => void; } const counter = createSlice<ICounterState, ICounterReducer>({ name: 'counter', initialState: { counter: 0, userInfo: { name: '', email: '', }, }, reducers: { addNumberCounter: (state: ICounterState, action: PayloadAction<number>) => { state.counter += action.payload; }, reduceNumberCounter: ( state: ICounterState, action: PayloadAction<number>, ) => { state.counter -= action.payload; }, }, extraReducers: (builder) => { builder .addCase(fetchUserInfo.pending, (state: ICounterState) => { console.log(state); //写加载的逻辑 }) .addCase( fetchUserInfo.fulfilled, ( state: ICounterState, action: PayloadAction<{ name: string; email: string }>, ) => { console.log('fulfilled', state, action); // 写成功的逻辑 state.userInfo.name = action.payload.name; state.userInfo.email = action.payload.email; }, ) .addCase( fetchUserInfo.rejected, ( state: ICounterState, action: PayloadAction<unknown, string, unknown, SerializedError>, ) => { console.log('reject', state, action.error); // 写错误的逻辑 }, ); }, }); // 导出action export const { addNumberCounter, reduceNumberCounter } = counter.actions; // 导出reducer export default counter.reducer;

在出口处导出类型

import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './modules/counter';
import {
  TypedUseSelectorHook,
  useSelector as useReduxSelector,
  useDispatch as useReduxDispatch,
} from 'react-redux';

const store = configureStore({
  reducer: { counter: counterReducer },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

export const useSelector: TypedUseSelectorHook<RootState> = useReduxSelector;
export const useDispatch = () => useReduxDispatch<AppDispatch>();   使用hook的时候在这里进行引入 useSelector, useDispatch

export default store;

 

这样就可以在组件中进行正常使用了

import { connect } from 'react-redux';
import { PureComponent, ReactElement } from 'react';
import {
  addNumberCounter,
  reduceNumberCounter,
  fetchUserInfo,
} from '../store/modules/counter';
import { AppDispatch, RootState } from '../store';

interface IContentProps {
  counter: number;

  userInfo: { name: string; email: string };

  addCounter: (num: number) => void;

  reduceCounter: (num: number) => void;

  fetchUserInfo: () => Promise<void>;
}

class Content extends PureComponent<IContentProps> {
  public render(): ReactElement {
    const { counter, addCounter, reduceCounter, userInfo, fetchUserInfo } =
      this.props;
    return (
      <div>
        <div>
          <span>counter: </span>
          <span>{counter}</span>
        </div>
        <div>
          <button onClick={() => addCounter(1)}>+1</button>
          <button onClick={() => reduceCounter(1)}>-1</button>
        </div>
        <div>
          <div>
            <span>user: </span>
            <span>{userInfo.name}</span>
          </div>
          <div>
            <span>email: </span>
            <span>{userInfo.email}</span>
          </div>
          <button onClick={() => fetchUserInfo()}>获取用户信息</button>
        </div>
      </div>
    );
  }
}

// 这个是把state注入到props
const mapStateToProps = (state: RootState) => {
  return {
    counter: state.counter.counter,

    userInfo: state.counter.userInfo,
  };
};

// 这个是把方法注入的props
const mapDispatchToProps = (dispatch: AppDispatch) => ({
  addCounter: (num: number) => dispatch(addNumberCounter(num)),
  reduceCounter: (num: number) => dispatch(reduceNumberCounter(num)),
  fetchUserInfo: () => dispatch(fetchUserInfo()),
});

export default connect(mapStateToProps, mapDispatchToProps)(Content);

使用hook进行react-redux的调用,替代connect

import { FC, ReactElement } from 'react';
import { shallowEqual } from 'react-redux';
import { RootState, useSelector, useDispatch } from '../store';
import {
  addNumberCounter,
  reduceNumberCounter,
} from '../store/modules/counter';

const Content: FC = (): ReactElement => {
  const { counter } = useSelector(
    (state: RootState) => ({
      counter: state.counter.counter,
    }), // 这个是把需要用的redux数据映射到返回的json上,方便调用,即解构出来的counter值
    shallowEqual, // 这个参数是进行浅层比较,为了提高性能
  );

  const dispatch = useDispatch();
  return (
    <div>
      <h2>counter: {counter}</h2>
      <button onClick={() => dispatch(addNumberCounter(1))}>+1</button>
      <button onClick={() => dispatch(reduceNumberCounter(1))}>-1</button>
<button onClick={() => dispatch(fetchUserInfo())}>获取用户信息<button> </div> ); }; export default Content;

5、redux中间件初探

实现一个日志打印的中间件

import { AnyAction, configureStore, Dispatch } from '@reduxjs/toolkit';
import { ToolkitStore } from '@reduxjs/toolkit/dist/configureStore';
import counterReducer from './modules/counter';

const store = configureStore({
  reducer: { counter: counterReducer },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch<any>;

const log = (store: ToolkitStore) => {
  const next: Dispatch<AnyAction> = store.dispatch;

  const wrapDispatch = <T extends AnyAction>(action: T): T => {
    console.log('this is before dispatch');
    next(action);
    console.log(store.getState());
    console.log('this is after dispatch');

    return action;
  };

  store.dispatch = wrapDispatch;
};

log(store);

export default store;

 手动实现一个thunk

const thunk = (store: ToolkitStore) => {
  const next = store.dispatch;

  const dispatchThunk = <T extends AnyAction | Function>(action: T): T => {
    if (typeof action === 'function') {
      action(store.dispatch, store.getState);
    } else {
      next(action);
    }

    return action;
  };

  store.dispatch = dispatchThunk;
};

thunk(store)

实现一个applyMiddleWare

 

posted on 2021-05-04 22:58  even_blogs  阅读(101)  评论(0)    收藏  举报