Skip to content

react-redux

使用心得

  1. 避免使用 connect 函式,改用 useSelectoruseDispatch
  2. 能很好的支持 redux 浏览器插件
  3. 不可以直接修改状态
  4. 异步处理相对复杂

安装

文档参考

ts声明处理

ts
import counterReducer from '@/features/counter/counterSlice';
import { combineReducers, configureStore } from '@reduxjs/toolkit';

const rootReducer = combineReducers({
  counter: counterReducer,
  // ...其他 reducers
});

export type RootState = ReturnType<typeof rootReducer>;

export default configureStore({
  reducer: rootReducer,
});

扩展

异步请求(方法1)

tsx
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';

// 创建异步 action
export const getList = createAsyncThunk('counter/getList', async (_, { rejectWithValue }) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ name: 'zhangsan' });
    }, 1000);
  });
});
export const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    data: {},
    loading: false,
  },
  extraReducers: (builder) => {
    /** 方法1 */
    builder
      .addCase(getList.pending, (state) => {
        state.loading = true;
      })
      .addCase(getList.fulfilled, (state, action) => {
        state.data = action.payload;
        state.loading = false;
      })
      .addCase(getList.rejected, (state) => {
        state.loading = false;
      });
    /**
     * 方法2
     * addMatcher 是全局处理
     * */
    // builder.addMatcher(
    //   (action) => action.type.endsWith('/pending'),
    //   (state) => {
    //     state.loading = true;
    //   },
    // );
    // builder.addMatcher(
    //   (action) => action.type.endsWith('/fulfilled'),
    //   (state, action) => {
    //     if (action.type === 'counter/getList/fulfilled') {
    //       state.data = action.payload;
    //     }
    //     state.loading = false;
    //   },
    // );
    // builder.addMatcher(
    //   (action) => action.type.endsWith('/rejected'),
    //   (state) => {
    //     state.loading = false;
    //   },
    // );
  },
});

export default counterSlice.reducer;
tsx
import { getList } from '@/features/counter/counterSlice';
import { RootState } from '@/store';
import { Spin } from 'antd';
import { useEffect } from 'react';
import { useSelector } from 'react-redux';
const About = () => {
  const loading = useSelector((state: RootState) => state.counter.loading);
  useEffect(() => {
    dispatch(getList());
  }, []);
  return (
    <div className="size-full">
      <Spin spinning={loading}>{JSON.stringify(data)}</Spin>
    </div>
  );
};
export default About;

异步请求(方法2)

ts
export const getList = () => async (dispatch) => {
  return new Promise((resolve) => {
    dispatch({ type: 'counter/setLoading', payload: true });
    setTimeout(() => {
      dispatch({ type: 'counter/setData', payload: { name: 'zhangsan' } });
      dispatch({ type: 'counter/setLoading', payload: false });
      resolve();
    }, 1000);
  });
};
export const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    data: {},
    loading: false,
  },
  reducers: {
    setData(state, { payload }) {
      state.data = payload;
    },
    setLoading(state, { payload }) {
      state.loading = payload;
    },
  },
});