又是 middleware。
簡述
就跟 Express 裡面的 middleware 差不多,Express 的 middleware 是把 request 經過處理後再回傳 response,在 reudx 的 middleware 則是把 action 先經過一段處理後再丟給 reducer 來處理。
簡單來說,原本我們的 action 只能是一個 Object:
1 2 3 4 5 6 7 8 9 10
| const ADD_TODO = 'addTodo'
function addTodo(name) { return { type: ADD_TODO, payload: { name } } }
|
但加上 middleware 以後,action 變成可以是一個 function:
1 2 3 4 5 6 7
| function addTodoAsync (name) { return function (dispatch) { setTimeout(() => { dispatch(addTodo(name)); }, 1000) } }
|
這個 function 會被傳入一個 dispatch,你只要把真正要 dispatch 的actionn 寫在裡面就行了。而這也就是 redux-think 這套 middleware 在做的事情
幫你執行 function
就這樣,沒了。
如果還是不太清楚的話,建議參考官方文件的圖,非常一目了然。
範例
這邊沿用 用 Redux-toolkit 串接 React 和 Redux 的範例來做個簡單的示範:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import { createSlice } from "@reduxjs/toolkit";
const initialState = { todos: [] }
let id = 0;
export const todosSlice = createSlice({ name: 'todos', initialState, reducers: { addTodo: (state, action) => { state.todos.push({ id: id++, name: action.payload }); }, deleteTodo: (state, action) => { state.todos = state.todos.filter(todo => todo.id !== action.payload); } } })
export const addTodoAsync = name => dispatch => { setTimeout(() => { dispatch(addTodo(name)) }, 1000) }
export const { addTodo, deleteTodo } = todosSlice.actions;
export const selectTodos = state => state.todosReducer.todos;
export default todosSlice.reducer
|
接著回到 React Component 裡面就能直接使用了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import { selectTodos } from "./features/todos/todosSlice"; import { deleteTodo, addTodoAsync } from './features/todos/todosSlice'; import { useDispatch ,useSelector } from "react-redux"; import { useState } from "react";
function App() {
const [value, setValue] = useState(""); const todos = useSelector(selectTodos);
const dispatch = useDispatch(); const handleSubmit = e => { e.preventDefault(); if (!value) return; dispatch(addTodoAsync(value)); setValue(""); }
return ( <div className="App"> <h1>Todos</h1> <form onSubmit={handleSubmit}> <input type="text" value={value} onChange={e => setValue(e.target.value)} /> </form> <ul> {todos.map(todo => ( <li key={todo.id}> {todo.name} <button onClick={() => dispatch(deleteTodo(todo.id))}>delete</button> </li> ))} </ul> </div> ); }
export default App;
|
現在就能用非同步的方式來新增 Todo,想看效果的話可以去我寫的範例來試。