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)

redux
http://yoursite.com/2022/04/13/react_redux/
作者
tatekii
发布于
2022年4月13日
许可协议