redux
本文最后更新于:2022年4月20日 下午
Redux
核心
Store
状态容器-
store.dispatch({type:'xxx'})
触发 action -
store.subscribe(()=>{...})
订阅状态 -
store.getState()
获取状态
-
Reducer
操作状态并返回新状态Action
描述对状态进行何种操作
Actions --> Reducer --> Store
Store -[subscribe]-> View
View -[dispatch]-> Actions
中间件
Action
优先被middleware
处理
// 模版格式
middleware = (store) => (next) => (action) => {
//...
next(action);
};
- logger 中间件
export default logger = (store) => (next) => (action) => {
console.log(store, action);
next(action);
};
redux-thunk
将action
改造为一个接收dispatch
参数的函数
- simple_thunk
const thunk = (store) => (next) => (action) => {
if (typeof action === "function") {
// 处理异步操作,action为一个函数
const { dispatch } = store;
return action(dispatch);
} else {
next(action);
}
};
- action
// async action
export const increase = (n = 1) => ({ type: INCREASE, payload: n });
export const increase_async =
(n = 1) =>
// 接收dispatch
(dispatch) => {
console.log("async");
setTimeout(() => {
dispatch(increase(n));
}, 2000);
};
- 挂载
const store = createStore(reducers, applyMiddleware(thunk));
redux-saga
可以将异步操作从action creater
中抽离出来
- action
// 和同步的action保持了一样的写法
export const toggle_async = (payload) => ({ type: "toggle_async", payload });
- saga 文件
import { takeEvery, put } from "react-saga/effects";
function* toggle_async_fn(action) {
yield put(toggle(action.payload)); //执行action
}
export default function* modalSaga() {
// 接收actions
yield takeEvery("toggle_async", toggle_async_fn);
}
- 挂载
import createSagaMiddleware from "redux-saga";
const sagaMiddleware = createSagaMiddleware();
const store = createStore(reducers, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(modalSaga);
redux-actions
使用模版方法创建 action
- action
export const increase = createAction("INCREASE");
- reducer
export default countReducer = handleActions({
new Map([
[
increase,
(state, action) => ({
counter: state.counter + action.payload
})
]])
})
toolkit
简化 redux 流程的工具集
createSlice
+ configureStore
import { createSlice, configureStore } from "@reduxjs/toolkit";
// reducer 的key
export const TODO_FEATURE_KEY = "TODOS";
const { reducer: TodoReducer, actions } = createSlice({
name: TODO_FEATURE_KEY,
initialState: [],
reducers: {
addTodo: {
reducer: (state, action) => {
state.push(action.payload);
},
prepare: (todoItem) => {
return { payload: { date: JSON.stringify(new Date()), title: item } };
},
},
},
});
// 配置store
const TodoStore = configureStore({
reducer: { [TODO_FEATURE_KEY]: TodoReducer },
devTools: process.env !== "production",
});
export const { addTodo } = actions;
export default TodoStore;
useSelect
+ useDispatch
import { useSelector, useDispatch } from "react-redux";
import { addTodo, TODO_FEATURE_KEY } from "../store/slices";
const Todo = () => {
const inputRef = useRef(null);
// dispatch
const dispatch = useDispatch();
// state
const todos = useSelector((state) => state[TODO_FEATURE_KEY]);
const handleAddTodo = () => {
dispatch(addTodo(inputRef.current.value));
inputRef.current.value = "";
};
};
createAsyncThunk
- dispatch 别的 action
export const addTodo_async = createAsyncThunk(`${TODO_FEATURE_KEY}/addTodo_async`, async (payload, thunkApi) => {
const p = await new Promise((resolve) => {
setTimeout(() => {
resolve(payload);
}, 2000);
});
thunkApi.dispatch(addTodo(p));
});
- 按照普通方式声明,在
extraReducer
中处理
export const addTodo_async2 = createAsyncThunk(`${TODO_FEATURE_KEY}/addTodo_async2`, async (payload) => {
return await new Promise((resolve) => {
setTimeout(() => {
resolve(payload);
}, 2000);
});
});
// reducerCreater
reducers:{
// build callback风格
extraReducers: (builder) => {
builder.addCase(addTodo_async2.fulfilled, (state, action) => {
action.payload = {date:(new Date()).toString(),title:action.payload}
state.push(action.payload);
})}
// Map Object 风格
extraReducers:{
[addTodo_async2.fulfilled]: (state, action) => {
state.push({date:(new Date()).toString(),title:action.payload});
}
}
}
-
createEntityAdapter
+createSelector
// {
// ids:[],
// entities:[]
// }
const todoAdapter = createEntityAdapter(
// {
// selectId:x=>x.xxId// 手动指定id
// 必须有ID,默认就是entity => entity.id
// sortComparer: (a, b) => a.title.localeCompare(b.title),// 排序方法
// }
);
const { reducer: TodoReducer, actions } = createSlice({
name: TODO_FEATURE_KEY,
initialState: todoAdapter.getInitialState(),
reducers: {
addTodo: {
// reducer: (state, action) => {
// todoAdapter.addOne(state,action.payload)
// },
// 简化为
reducer: todoAdapter.addOne
},
},
});
const {selectAll} = todoAdapter.getSelector()
export const todoSelector = createSelector(state=>state[TODO_FEATURE_KEY],selectAll)
// 组件中
const todos = useSelector(todoSelector)
// 优化前
const todos = useSelector(state=>state[TODO_FEATURE_KEY].entities)