乐趣区

关于redux:想了解关于-Redux-的这里都有

一、Redux 外围

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

const state = {
    modleOpen: "yes",
    btnClicked: "no",
    btnActiveClass: "active",
    page: 5,
    size: 10
}
  1. 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 属性向上层组件传递数据,而上层组件不能向下层组件传递数据,要实现上层组件批改数据,须要
下层组件传递批改数据办法到上层组件,当我的项目越来越大的时候,组件之间传递数据也就变得越来越艰难。

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

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

3. 下载 Redux

npm install redux react-redux

4.Redux 工作流程

  1. 组件通过 dispatch 办法触发 action
  2. Store 接管 Action 并将 Action 分发给 Reducer
  3. Reducer 依据 Action 类型对状态进行更改并将更改后的状态返回给 Store
  4. 组件订阅了 Store 中的状态,Store 中的状态更新会同步到组件

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 持续解决

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'));
    }, [])
退出移动版