react-redux 详解 redux-promise
使用场景
- 某个组件的状态,需要共享
- 某个状态需要在任何地方都可以拿到
- 一个组件需要改变全局状态
- 一个组件需要改变另一个组件的状态
设计思想
- Web 应用是一个状态机,视图与状态是一一对应的;
- 所有的状态,保存在一个对象里面。
基本原则
- 整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中
- 唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象
- 编写 reducers为了描述 action 如何改变 state tree
基本概念
- 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>
);
- 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};
- 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;
}
}
- 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);
- 异步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接口写好了,对接后补充此模块
- 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;