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);
  });
};

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';

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

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

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 { useSelector, useDispatch, shallowEqual } from 'react-redux';
import { RootState } 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>
    </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  阅读(64)  评论(0编辑  收藏  举报