react-redux 详解 redux-promise

使用场景

  1. 某个组件的状态,需要共享
  2. 某个状态需要在任何地方都可以拿到
  3. 一个组件需要改变全局状态
  4. 一个组件需要改变另一个组件的状态

设计思想

  1. Web 应用是一个状态机,视图与状态是一一对应的;
  2. 所有的状态,保存在一个对象里面。

基本原则

  1. 整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中
  2. 唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象
  3. 编写 reducers为了描述 action 如何改变 state tree

基本概念

  1. Store

(1)定义:用于存储整个应用的 state 数据的唯一容器
(2)使用:
① 新建文件夹 store,再添加 index.js,, 内容如下:

import { legacy_createStore as createStore, combineReducers} from 'redux'
import countReducer from "./reducer/countReducer"
import listReducer from "./reducer/listReducer"
import objReducer from './reducer/objReducer';
export default createStore(combineReducers({
  countReducer,
  listReducer,
  objReducer
}));

createStore函数接受纯函数作为参数,返回新生成的 Store 对象;
combineReducers 将多个 reducer (处理不同数据时,需要抽离多个reducer)合并成为一个;

② 在 src/index.js 中引入 src/store/index.js
react-redux 提供Provider组件,让所有子组件都可以拿到state

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from "react-router-dom";
import { Provider } from 'react-redux/es/exports';
import store from './store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Provider store={store}>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </Provider>
);
  1. action
    ① 是一个对象,约定必须有type 字段来表示将要执行的动作,其他字段自定义
    ② 数据从view传到 store 的有效载荷,是 store 数据的唯一来源
    ③ 使用store.dispatch()是 View 发出 Action 的唯一方法
//  src/store/action.js
function add() {
  return {
      type: 'ADD',
  }
}
function minus() {
  return {
      type: 'MINUS'
  }
}
export default {add, minus};
  1. reducer
    ① 指定了view状态的变化如何♤响应 actions 并发送到 store;
    ② reducer 一定要保持纯净
    ③ 不要修改 state,使用 Object.assign({}, state, newState}),或 扩展运算符
    ④ 在 default 情况下返回旧的 state。遇到未知的 action 时,一定要返回旧的 state
    ⑤ state = 0 用于设置默认值
//  src/store/reducer/countReducer .js
import types from "../constant";
export default function countReducer (state = 0, action) {
  switch (action.type) {
    case types.ADD:
      return state + 1;
    case types.MINUS:
      return state - 1;
    default:
      return state;
  }
}
  1. connect
    ① 用于从展示组件生成容器组件
    ② connect方法接受两个参数:mapStateToProps和mapDispatchToProps;
    mapStateToProps方法来指定如何把当前 state 映射到展示组件的 props 中;
    mapDispatchToProps方法接收 dispatch() 方法并返回期望注入到展示组件的 props 中的回调方法
// S 是实现组件,默认导出容器组件
import React from "react";
import {connect} from "react-redux";
import types from "../../../store/constant";
class S extends React.Component {
  render () {
    return (
      <div>
         <p>store-count: {this.props.count}</p>
         <button onClick={() => {this.props.add()}}>+</button>
         <button onClick={() => {this.props.minus()}}>-</button>
         <ul>
           {this.props.list.map((v, i) => {
             return (<li key={i}>{v}</li>)
           })}
         </ul>
       </div>
    )
  }
}
export default connect(
  state => ({
    count: state.countReducer,
    list: state.listReducer
  }),
  dispatch => ({
    add: () => {
      dispatch({type: types.ADD_LIST})
    },
    minus: () => {
      dispatch({type: types.MINUS_LIST})
    },
  })
)(S);
  1. 异步Action
    5.1 redux-thunk
    安装 redux-thunk 包:
npm install  redux-thunk

在 Redux 应用中引入 redux-thunk 中间件,并将其添加到 Redux Store 的中间件链中。

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const store = createStore(
  rootReducer,
  applyMiddleware(thunk)
);

在组件中使用

import { useDispatch } from "react-redux";
import { Button } from 'antd';
import userApi from "../../api/user";

function Asy () {
  let dispatch = useDispatch();
  const actionFn = (type, response) => {
    return {type, payload};
  }
  const fetchPosts = postTitle => (dispatch, getState) => {
     dispatch(actionFn ("FETCH_DATA_PENDING"));
     return userApi.userList({
         "username": "",
         "insDateStartStr": "2023-01-01 00:00",
         "insDateEndStr": "2023-12-31 23:59",
         "pageSize": 10,
         "pageNum": 1
     }).then(res => {
       let data = res.data || {};
       dispatch(actionFn("FETCH_DATA_FULFILLED", data))
     }).catch(err => {
       dispatch(actionFn("FETCH_DATA_REJECTED", err))
     })
  };
  return (
    <div>
      <Button type="primary" onClick={handleClick}>query</Button>
    </div>
  )
}
export default Asy;

5.2 redux-promise
允许你在 Redux 中处理异步操作,并且简化了异步操作的状态管理过程。
使用 redux-promise 需要进行以下步骤:
安装 redux-promise 包:

npm install redux-promise

在 Redux 应用中引入 redux-promise 中间件,并将其添加到 Redux Store 的中间件链中。

import { createStore, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise';
import rootReducer from './reducers';

const store = createStore(
  rootReducer,
  applyMiddleware(promiseMiddleware)
);

定义 Action Creator,用于创建带有异步操作的 Action。

import axios from 'axios';

export const fetchData = () => {
  return {
    type: 'FETCH_DATA',
    payload: axios.get('/api/data') // 异步操作,返回一个 Promise
  };
};

在 Reducer 中处理异步操作的 Action。

const initialState = {
  data: null,
  loading: false,
  error: null
};

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case 'FETCH_DATA_PENDING':
      return { ...state, loading: true };
    case 'FETCH_DATA_FULFILLED':
      return { ...state, loading: false, data: action.payload.data };
    case 'FETCH_DATA_REJECTED':
      return { ...state, loading: false, error: action.payload };
    default:
      return state;
  }
};

参照 Redux 入门教程(二):中间件与异步操作 - 阮一峰 等node接口写好了,对接后补充此模块

  1. Hook - useReducer
    可以用于替代 useState 来处理更复杂的状态逻辑,尤其是当状态之间存在相互关联或需要进行复杂的状态转换时。
    useReducer 函数接受一个 reducer 函数和一个初始状态作为参数,并返回一个包含当前状态和状态更新函数的数组。reducer 函数接收当前的状态和一个动作对象作为参数,并根据动作类型来更新状态。
    示例用法如下:
import React, { useReducer } from 'react';

// 定义 reducer 函数
const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

const Counter = () => {
  // 使用 useReducer 创建状态和状态更新函数
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
    </div>
  );
};

export default Counter;