likes
comments
collection
share

想了解关于 Redux 的这里都有

作者站长头像
站长
· 阅读数 34

一、Redux 核心

官方是这样解释Redux的:JavaScript 状态容器,提供可预测化的状态管理。

const state = {
    modleOpen: "yes",
    btnClicked: "no",
    btnActiveClass: "active",
    page: 5,
    size: 10
}
  1. Redux 核心概念及工作流程想了解关于 Redux 的这里都有
  2. store: 存储状态的容器,JavaScript对象
  3. View: 视图,HTML页面
  4. Actions: 对象,描述对状态进行怎样的操作
  5. Reducers: 函数,操作状态并返回新的状态
  6. Redux 计数器案例 ../Redux/src/counter

    <body>
     <button id="plus">+</button>
     <span id="count">0</span>
     <button id="minus">-</button>
    <script src="https://cdn.bootcdn.net/ajax/libs/redux/4.2.0/redux.min.js"></script>
    <script>
    
     // 3 存储默认状态
     let initialState = {
         count: 0
     }
     // 2 创建 reducer 函数
     function reducer (state = initialState, action) {
         // 7 接收 action 并判断 action 的类型
         switch (action.type) {
             case 'increment':
                 return {count : state.count + 1}
             case 'decrement':
                 return {count : state.count - 1}
             default:
                 // 初始化是会自动发送一个 init 的 action 用来存储默认的state
                 return state;
         }
     }
     // 1 创建 store 对象, createStore 有第二个参数代表默认值,也就是 reducer 中的 state 参数
     let store = Redux.createStore(reducer);
    
     // 4 定义 action
     let increment = { type: 'increment' }
     let decrement = { type: 'decrement' }
    
     // 5 获取按钮并添加事件
     document.getElementById('plus').onclick = function() {
         // 6 触发action
         store.dispatch(increment);
     }
     document.getElementById('minus').onclick = function () {
         store.dispatch(decrement);
     }
    
     // 8 订阅 store,当store发生变化的时候会执行回调
     store.subscribe(() => {
         // 获取 store 中存储的状态
         console.log(store.getState())
         document.getElementById('count').innerText = store.getState().count;
     });
    
    </script>
    </body>
  7. Redux核心API
  8. const store = Redux.crateStore(reducer): 创建 Store 容器
  9. function reducer (state = initialState, action) {}: 创建用于处理状态的 reducer 函数
  10. store.getState(): 获取状态
  11. store.subscribe(function(){}): 订阅状态
  12. store.dispatch({type: 'discription...'}): 触发action

二、React + Redux

1.在 React 中不使用 Redux 时遇到的问题

在 React 中组件通信的数据流是单向的,顶层组件可以通过props属性向下层组件传递数据,而下层组件不能向上层组件传递数据,要实现下层组件修改数据,需要上层组件传递修改数据方法到下层组件,当项目越来越大的时候,组件之间传递数据也就变得越来越困难。想了解关于 Redux 的这里都有

2.在 React 项目中引入 Redux 的好处

使用Redux管理数据,由于Store独立于组件,使得数据管理独立于组件,解决了组件与组件之间传递数据困难的问题。

想了解关于 Redux 的这里都有

3.下载Redux

npm install redux react-redux

4.Redux 工作流程

  1. 组件通过 dispatch 方法触发 action
  2. Store 接收 Action 并将 Action 分发给 Reducer
  3. Reducer 根据 Action 类型对状态进行更改并将更改后的状态返回给Store
  4. 组件订阅了Store中的状态,Store中的状态更新会同步到组件想了解关于 Redux 的这里都有

5.代码案例

计数器案例 ../Redux/react-redux-guide/src/components/Counter.js弹出框案例 ../Redux/react-redux-guide/src/components/Modal.js

6.拆分Reducer

../Redux/react-redux-guide/src/react-redux-guide/src/store/reducers/root.reducer.js

三、Redux 中间件

中间件本质上就是一个函数,Redux允许我们通过中间件的方式,扩展和增强Redux应用程序,增强体现在对action处理能力上,之前的计数器与弹出框案例中。actions都是直接被reducer函数处理的,再加入了中间件以后,在出发了一个action之后这个action会优先被中间件处理,当中间处理完这个action以后,中间件会把这个action传递给reducer,让reducer继续处理想了解关于 Redux 的这里都有

1.开发Redux中间件

开发中间件模版代码,本质上是一个函数,并且是一个柯里化的一个函数

export default store => next => action => {}

这个函数中要求我们返回一个函数,在这个返回的函数中要求我们再返回一个函数,在最里层的函数中我们可以执行我们自己的业务逻辑,在最外层的函数中给我们提供了一个参数叫store,可以用store.getState获取当前state状态,也可以使用store.dispatch来触发另外一个action,至于干什么具体根据使用的业务逻辑来决定。在最里层的函数也有一个形参,这个形参就是组件触发的action对象,可以根据action.type来决定是否对当前action进行处理。中间的函数也有一个参数,这个参数是一个函数,我们称之为next,在我们执行完了逻辑代码之后,我们要去调用next方法,把当前action 传递给reducer,或者说传递给下一个中间件,因为中间件可以有多个。中间件开发好之后需要引入我们写的中间件并且注册给redux。

2.注册中间件

中间件在开发完成以后只有被注册才能在Redux的工作流程中生效。

import { createStore, applyMiddleware } from 'redux'
import logger from './middlewares/logger'

createStore(reducer, applyMiddleware(
    logger
))

代码案例:src/react-redux-guide/src/store/middleware

/**
 * src/react-redux-guide/src/store/middleware/logger.js
 * 开发中间件 
 * 中间件模板代码 export default store => next => action => {}
 */
export default store => next => action => {
  console.log(action);
  console.log(store);
  next(action); // 只有调用了next方法才可以将action传递给下一个中间件或者reducer
}
/**
 * src/react-redux-guide/src/index.js
 */
import { createStore, applyMiddleware } from 'redux';
import RootReducer from "./reducers/root.reducer";
import logger from "./middleware/logger";
import test from "./middleware/test";

// 注册中间件 applyMiddleware(logger); 中间件的执行顺序取决去注册的顺序

export default createStore(RootReducer, applyMiddleware(logger, test));
  • 中间件通用性改造

    /**
     * src/react-redux-guide/src/store/middleware/thunk.js
     */
    import actionTypes from "../actionTypes";
    export default store => next => action => {
    // if(action.type === actionTypes.countIncrementType || action.type === actionTypes.countDecrementType) {
    //   // 开启延迟操作
    //   setTimeout(() => {
    //     next(action);
    //   },2000)
    // }
    
    // 1.当这个中间件函数不关心你想执行什么异步操作,只关心你执行的是不是异步操作
    // 2.如果你执行的是异步操作,再你触发action的时候,给中间件传递一个函数,如果执行的是同步操作就传递一个正常action对象
    // 3.异步操作代码要写在传递进来的函数中
    // 4.当前这个中间件的函数在点用你传递进来的函数时,要将dispatch方法传递进去
    
    if(typeof action === 'function') {
      return action(store.dispatch);
    }
    next(action);
    }
/**
 * src/react-redux-guide/src/store/middleware/thunk.js
 */
import actionTypes from './actionTypes';

const actions =  {
  // Counter
  countIncrementAction: () => ({type: actionTypes.countIncrementType}),
  countDecrementAction: () => ({type: actionTypes.countDecrementType}),
  // 异步的action
  countIncrementActionAsync: () => dispatch => {
    setTimeout(() => {
      dispatch(actions.countIncrementAction());
    }, 2000);
  },

  // Modal
  boxShowAction: () => ({type: actionTypes.boxShowType}),
  boxHiddenAction: () => ({type: actionTypes.boxHiddenType}),
  boxShowActionAsync: () => dispatch => {
    setTimeout(() => {
      dispatch(actions.boxShowAction());
    }, 2000)
  }
}
export default actions;

3.Redux 常用中间件

1.redux-thunk

redux-thunk 允许我们在redux的工作流程中使用异步操作,与自定义thunk中间件用法相同(src/react-redux-guide/src/store/middleware/thunk.js)安装:npm install redux-thunk

允许action返回一个函数,这个函数接受一个dispatch参数,在这个函数当中做异步操作。

2.redux-thunk

redux-sage 可以将异步操作从ActionCreator文件中抽离出来,放在一个单独的文件中

1.创建 redux-sage 中间件

import createSagaMiddleware from 'redux-saga';
const sagaMiddleware = createSagaMiddleware();

2.注册sagaMiddleware

createStore(reducer, appleMiddleware(sagaMiddleware));

3.使用 saga 接收 action 执行一步操作

/**
 * src/react-redux-guide/src/store/sagas/counter.saga.js
 */
import { takeEvery, put, delay} from 'redux-saga/effects';
import actionTypes from "../actionTypes";
import actions from "../actionCreators";

/**
 * takeEvery: 接收 action
 * put: 触发 action
 * delay: 延迟(这里不可以用setTimeout)
 */

function* countIncrement() {
  yield delay(2000);
  yield put(actions.countIncrementAction());
}

export default function* counterSaga () {
  yield takeEvery(actionTypes.COUNTINCREMENTSAGATYPE, countIncrement)
}

4.启用saga

/**
 * src/react-redux-guide/src/store/index.js
 */
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk'; // 官方中间件
import createSagaMiddleware from 'redux-saga';
import counterSaga from './sagas/counter.saga'
import RootReducer from "./reducers/root.reducer";
// 自定义中间件
// import logger from "./middleware/logger";
// import test from "./middleware/test";
// import thunk from "./middleware/thunk";

// 注册中间件 applyMiddleware(logger); 中间件的执行顺序取决去注册的顺序


// 创建 saga
const sagaMiddleware = createSagaMiddleware();

// export default createStore(RootReducer, applyMiddleware(thunk));
export default createStore(RootReducer, applyMiddleware(sagaMiddleware));

// 启动 saga
sagaMiddleware.run(counterSaga)

5.saga 中 action 传值

// 可以接收到 action 返回的对象
function* countIncrement(action) {
  yield delay(2000);
  // 获取 action 中的值并传递给下一个action
  yield put(actions.countIncrementAction(action.value));
}

export default function* counterSaga () {
  yield takeEvery(actionTypes.COUNTINCREMENTSAGATYPE, countIncrement)
}

6.saga拆分合并

/**
 * src/react-redux-guide/src/store/sagas/root.saga.js
 */
import { all } from 'redux-saga/effects';
import counterSaga from "./counter.saga";
import modalSaga from "./modal.saga";

// 合并Saga
export default function* rootSaga() {
  yield all([
    counterSaga(),
    modalSaga()
  ])
}
/**
 * src/react-redux-guide/src/store/index.js
 */
// 修改启动 saga
sagaMiddleware.run(rootSaga);

4.redux-actions

redux流程中大量的样板代码编写是非常痛苦的,使用 redux-actions可以简化action和reducer的处理

  1. 下载npm install redux-actions
  2. 创建 Action

    /**
     * src/react-redux-guide/src/store/actions/counter.actions.js
     */
    import { createAction } from 'redux-actions';
    
    // 接收和触发action只需要使用 createAction() 的返回值就可以
    export const incrementAction = createAction('increment');
    export const decrementAction = createAction('decrement');
    
  3. 创建Reducer

    /**
     * src/react-redux-guide/src/store/reducers/counter.reducer.js
     */
    import { handleActions as createReducer } from 'redux-actions';
    import * as counterActions from '../actions/counter.actions';
    
    /**
     * handleActions 返回值就是reducer函数
     */
    
    const initialState = {
      count: 0,
    }
    
    const handleIncrement = (state, action) => ({count: state.count + 1})
    const handleDecrement = (state, action) => ({count: state.count - 1})
    
    export default createReducer({
      [counterActions.incrementAction]: handleIncrement,
      [counterActions.decrementAction]: handleDecrement,
    }, initialState);

    3.redux-actions传值

redux-action 传值并不需要再actions中去接收,默认会挂载到 action.payload 的属性中

/**
 * src/react-redux-guide/src/components/Counter.js
 */
import React from 'react';
import { connect } from 'react-redux';
import * as counterActions from '../store/actions/counter.actions';
const Counter = (props) => {
  const {count, handleCountPlus, handleCountMinus} = props;
  return (
    <section>
      <button onClick={() => handleCountPlus(10)}>Plus</button>
      <span>{count}</span>
      <button onClick={() => handleCountMinus(10)}>Minus</button>
    </section>
  )
}

const mapStateToProps = state => ({
  count: state.counter.count
})

const mapDispatchToProps = dispatch => ({
  handleCountPlus(value) {
    dispatch(counterActions.incrementAction(value)) // redux-actions

  },
  handleCountMinus (value){
    dispatch(counterActions.decrementAction(value)) // redux-actions
  }
})

export default connect(mapStateToProps, mapDispatchToProps)(Counter);
/**
 * src/react-redux-guide/src/store/actions/counter.actions.js
 */
import { createAction } from 'redux-actions';

// 这里不需要去接收传递的值
// export const incrementAction = createAction('increment', value); 错误
export const decrementAction = createAction('decrement');
/**
 * src/react-redux-guide/src/store/reducers/counter.reducer.js
 */
// 使用 redux-action 简化reducer
const handleIncrement = (state, action) => ({count: state.count + action.payload})
const handleDecrement = (state, action) => ({count: state.count - action.payload})


export default createReducer({
  [counterActions.incrementAction]: handleIncrement,
  [counterActions.decrementAction]: handleDecrement,
}, initialState);

5. 实战案例

  • src/shopping-demo:前端文件

    • 启动 npm start
    • 端口 localhost:3000
  • src/shoppingCartService: 服务端文件

    • 启动 npm start
    • 端口 localhost:3005

四、 Redux 源码实现

五、Redux-toolkit

1.概述

redux toolkit 对 redux 进行的二次封装,用于高效的 Redux 开发,使 Redux 的使用变得简单。

2.快速入门

  1. 创建状态切片

对于状态切片,我们可以认为他就是原本的Redux中的那一个个小的reducer函数。

在Redux中,原本的 Reducer 函数和 Action 对象需要分别创建,现在通过状态切片来替代,它会返回 reducer 函数和 Action 对象。

import {createSlice} from "@reduxjs/toolkit";


const TODOS_FEATURE_KEY = 'todos';
const {reducer: todoReducer, actions } = createSlice({
  name: TODOS_FEATURE_KEY, // 切片名称
  initialState: [], // 切片状态
  reducers: {
    // 创建 actionCreator 和状态处理函数
    // addTodo 是 action 的名称
    addTodo: (state, action) => {
      // 在 redux-toolkit 中使用createSlice 可以直接修改 state 的值,而并不需要返回一个新的状态
      state.push(action.payload);
    }
  }
});

export const {addTodo} = actions;
export default todoReducer;

2.创建 store

import {configureStore} from "@reduxjs/toolkit";
import TodosReducer, {TODOS_FEATURE_KEY} from "./todos.slice";

export default configureStore({
  reducer: {
    [TODOS_FEATURE_KEY]: TodosReducer
  },
  devTools: process.env.NODE_ENV !== 'production'
});

3.配置Provider

import React from 'react';
import ReactDOM from 'react-dom/client';
import {Provider} from "react-redux";
import App from './App';
import store from "./Store";

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App/>
    </Provider>
  </React.StrictMode>
);

4.组件中触发Action

import {useDispatch, useSelector} from "react-redux";
import {TODOS_FEATURE_KEY, addTodo} from "../../Store/todos.slice";

const Todo = () => {
  // 获取dispatch方法
  const dispatch = useDispatch();
  // 获取最新的状态
  const todos = useSelector(state => state[TODOS_FEATURE_KEY]);

  return (
          <div>
            <button onClick={() => dispatch(addTodo({title: '测试任务'}))}>添加任务</button>
            <ul>
              {
                      todos && todos.map(todo => (
                              <li key={todo.title}>{todo.title}</li>
                      ))
              }
            </ul>
          </div>
  )
}

export default Todo;
  1. action 预处理

当 Action 被触发后,可以通过prepare方法进行预处理,处理完成后交给Reduce.prepare方法必须返回对象

import {createSlice} from "@reduxjs/toolkit";

export const TODOS_FEATURE_KEY = 'todos';

// 使用 reducer.prepare 进行 action 预处理
const {reducer: TodosReducer, actions } = createSlice({
  name: TODOS_FEATURE_KEY, // 切片名称
  initialState: [], // 切片状态
  reducers: {
    // 创建 actionCreator
    addTodo: {
      // 状态处理函数
      reducer: (state, action) => {
        console.log('reducer', action);
        state.push(action.payload)
      },
      // action预处理,必须返回一个对象 {payload: {...todo, title: 'haha'}}, 覆盖原有的payload
      prepare: todo => {
        console.log('prepare', todo)
        // 修改 action 传过来的数据
        return {
          payload: {id: Math.random(), ...todo}
        }
      }
    }
  }
});

export const {addTodo} = actions;
export default TodosReducer;
  1. 执行异步的操作方式

第一种方式


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

export const TODOS_FEATURE_KEY = 'todos';

/**
 * 异步操作:第一种
 * createAsyncThunk('action-type', (payload, thunkAPI) => {异步操作})
 */

// 定义请求方法
const loadTodos = createAsyncThunk('async-load-todos',  (payload, thunkAPI) => {
  axios.get(payload)
    .then(res => {
      // 触发保存数据action setTodos
      thunkAPI.dispatch(setTodos(res.data));
    })
    .catch(err => new Error(err))
});

// 使用 reducer.prepare 进行 action 预处理
const {reducer: TodosReducer, actions } = createSlice({
  name: TODOS_FEATURE_KEY, // 切片名称
  initialState: [], // 切片状态
  reducers: {
    // 创建 actionCreator
    addTodo: {
      // 状态处理函数
      reducer: (state, action) => {
        console.log('reducer', action);
        state.push(action.payload)
      },
      // action预处理,必须返回一个对象 {payload: {...todo, title: 'haha'}}, 覆盖原有的payload
      prepare: todo => {
        console.log('prepare', todo)
        // 修改 action 传过来的数据
        return {
          payload: {id: Math.random(), ...todo}
        }
      }
    },
    setTodos: (state, action) => action.payload
  }
});


// jsx
export const {addTodo, setTodos} = actions;
export {loadTodos};
export default TodosReducer;

import {TODOS_FEATURE_KEY, addTodo, loadTodos} from "../../Store/todos.slice";
import {useDispatch, useSelector} from "react-redux";
import {useEffect} from "react";

const Todo = () => {
  // // 获取dispatch方法
  const dispatch = useDispatch();
  // // 获取最新的状态
  const todos = useSelector(state => state[TODOS_FEATURE_KEY]);
  // // 组件挂载完成之后请求
  useEffect(() => {
    dispatch(loadTodos('http://localhost:3001/todos'));
  }, [])
  return (
          <div>
            <button onClick={() => dispatch(addTodo({title: '测试任务'}))}>添加任务</button>
            <ul>
              {
                      todos && todos.map(todo => (
                              <li key={todo.title}>{todo.title}</li>
                      ))
              }
            </ul>
          </div>
  )
}

export default Todo;

第二种方式

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

export const TODOS_FEATURE_KEY = 'todos';
/**
 * 异步操作:第二种
 * createAsyncThunk('action-type', payload => {异步操作})
 * 不需要再去编写保存数据的action, 可以直接在extraReducers中处理异步的action
 */

const loadTodos = createAsyncThunk('async-load-todos', payload => {
  return axios.get(payload)
    .then(res => res.data)
    .catch(err => new Error(err));
})

// 使用 reducer.prepare 进行 action 预处理
const {reducer: TodosReducer, actions } = createSlice({
  name: TODOS_FEATURE_KEY, // 切片名称
  initialState: [], // 切片状态
  reducers: {
    // 创建 actionCreator
    addTodo: {
      // 状态处理函数
      reducer: (state, action) => {
        console.log('reducer', action);
        state.push(action.payload)
      },
      // action预处理,必须返回一个对象 {payload: {...todo, title: 'haha'}}, 覆盖原有的payload
      prepare: todo => {
        console.log('prepare', todo)
        // 修改 action 传过来的数据
        return {
          payload: {id: Math.random(), ...todo}
        }
      }
    },
    setTodos: (state, action) => action.payload
  },
  // 第二种异步请求编写方式:extraReducers 处理异步请求
  extraReducers: {
    // 处理 loadTodos 返回的 Promise 对象的成功状态
    [loadTodos.fulfilled]: (state, action) => {
      console.log('fulfilled');
      return action.payload;
    },
    [loadTodos.pending]: (state, action) => {
      console.log('pending');
      return state;
    },
  }
});


export const {addTodo, setTodos} = actions;
export {loadTodos};
export default TodosReducer;

// jsx
import {TODOS_FEATURE_KEY, addTodo, loadTodos} from "../../Store/todos.slice";
import {useDispatch, useSelector} from "react-redux";
import {useEffect} from "react";

const Todo = () => {
  return (
    <div>
      <button onClick={() => dispatch(addTodo({title: '测试任务'}))}>添加任务</button>
      <ul>
        {
          todos && todos.map(todo => (
            <li key={todo.title}>{todo.title}</li>
          ))
        }
      </ul>
    </div>
  )
}

export default Todo;
  1. 配置中间件

    import {configureStore, getDefaultMiddleware} from "@reduxjs/toolkit";
    import TodosReducer, {TODOS_FEATURE_KEY} from "./todos.slice";
    import logger from 'redux-logger';
    
    export default configureStore({
      reducer: {
     [TODOS_FEATURE_KEY]: TodosReducer
      },
      /**
    * middleware: [] 默认隐式的会挂载 @reduxjs/toolkit 内置的中间件
    * 如果显示编写出来并添加自定义或者其他中间件时,需要通过getDefaultMiddleware()方法获取内置的中间件并挂载上去,不然内置的中间件就会被覆盖
    */
      middleware: [...getDefaultMiddleware(), logger],
      devTools: process.env.NODE_ENV !== 'production'
    });
  2. 实体适配器将状态放入实体适配器,实体适配器提供操作状态的各种方法,简化操作。

    • 创建实体适配器
    import {createEntityAdapter} from "@reduxjs/toolkit";
    import axios from 'axios';
    
    export const TODOS_FEATURE_KEY = 'todos';
    
    const loadTodos = createAsyncThunk('async-load-todos',  (payload, thunkAPI) => {
      axios.get(payload)
     .then(res => {
       // 触发保存数据action setTodos
       thunkAPI.dispatch(setTodos(res.data));
     })
     .catch(err => new Error(err))
    });
    
    /**
     * 创建实体适配器
     * todosAdapter.getInitialState(); // entities: {} 存储状态 ids: [] 存储每一条状态的 id
     */
    const todosAdapter = createEntityAdapter();
    
    const {reducer: TodosReducer, actions } = createSlice({
      name: TODOS_FEATURE_KEY, // 切片名称
      // 将状态放入适配器当中
      initialState: todosAdapter.getInitialState(),
      reducers: {
     // 创建 actionCreator
     addTodo: {
       // 状态处理函数
       reducer: (state, action) => {
         // 使用状态适配器向状态中添加数据
         todosAdapter.setOne(state, action.payload); // 添加一条
       },
       // action预处理,必须返回一个对象 {payload: {...todo, title: 'haha'}}, 覆盖原有的payload
       prepare: todo => ({payload: {id: Math.random(), ...todo}})
     },
     setTodos: (state, action) => todosAdapter.setMany(state, action.payload),
      },
    });
    
    
    export const {addTodo, setTodos} = actions;
    export {loadTodos};
    export default TodosReducer;
  • 简化实体适配器

    const {reducer: TodosReducer, actions } = createSlice({
    name: TODOS_FEATURE_KEY, // 切片名称
    // 将状态放入适配器当中
    initialState: todosAdapter.getInitialState(),
    reducers: {
      // 创建 actionCreator
      addTodo: {
        /**
         * 简化实体适配器函数,自动检测第二个是否是action参数,如果是action的话就会去找action.payload并将其放到state中
         */
        reducer: todosAdapter.setOne,
    
        // action预处理,必须返回一个对象 {payload: {...todo, title: 'haha'}}, 覆盖原有的payload
        prepare: todo => {
          console.log('prepare', todo)
          // 修改 action 传过来的数据
          return {
            payload: {id: Math.random(), ...todo}
          }
        }
      },
      setTodos: todosAdapter.setMany
    }
    });
  • 更改实体适配器的唯一表示

实体适配器要求每一个实体必须拥有 id 属性作为唯一标识,如果实体中的唯一标识字段不叫作 id, 需要使用 selectId 进行声明。

const todosAdapter = createEntityAdapter({ selectId: todo => todo.cid})
  • 状态选择器提供了从实体状态器中获取状态的快捷途径。

    import {createSelector} from '@reduxjs/toolkit';
    
    /**
     * 创建状态选择器
     * createSelector(相应的state, 具体的状态选择器(由实体适配器返回)
     */
    const {selectAll} = todosAdapter.getSelectors();
    export const selectorTodos = createSelector(state => state[TODOS_FEATURE_KEY], selectAll);
    
    
    // jsx
    const dispatch = useDispatch();
    
    // 使用状态选择器获取状态
    const todos = useSelector(selectorTodos);
    
    useEffect(() => {
    dispatch(loadTodos('http://localhost:3001/todos'));
    }, [])
转载自:https://segmentfault.com/a/1190000042071896
评论
请登录