兩個狀態管理工具:
- dva 是一個基于 Redux 和 React Router 的數據流方案,它提供了對 Redux 和 React Router 的封裝,使得在使用 dva 時可以更方便地進行狀態管理和路由操作。
- React Redux是一個JavaScript狀態管理庫,它的核心包括store、action和reducer。store負責存儲和管理狀態,action描述狀態變化,reducer則根據舊狀態和action計算新狀態。
簡單來說,
- useState可以直接更新狀態;
- useReducer通過dispatch多個action來更新狀態,reducer函數脫離了UI,可以獨立復用;
- useSelector負責從store全局中讀取和渲染狀態,useDispatch負責通過分發action,更新store狀態,二者都存在于React-Redux中;
- connect是存在于dva中,負責從store中讀取狀態,并傳遞給組件。
目錄
useState
useReducer
useSelector、useDispatch
connect
正文
-
useState
?直接更新狀態。
-
useReducer
?可以脫離了UI,可以獨立復用。但無法像redux一樣進行跨組件的狀態共享。
對于擁有許多狀態更新邏輯的組件來說,過于分散的事件處理程序可能會令人不知所措。對于這種情況,你可以將組件的所有狀態更新邏輯整合到一個外部函數中,這個函數叫作 reducer。
使用 reducer 管理狀態與直接設置狀態略有不同。它不是通過設置狀態來告訴 React “要做什么”,而是通過事件處理程序 dispatch 一個 “action” 來指明 “用戶剛剛做了什么”。(而狀態更新邏輯則保存在其他地方!)
function handleAddTask(text) {
// "action" 對象:dispatch({type: 'added',id: nextId++,text: text,});
}function handleChangeTask(task) {dispatch({type: 'changed',task: task,});
}function handleDeleteTask(taskId) {dispatch({type: 'deleted',id: taskId,});
}
“action”是一個普通的 JavaScript 對象。它的結構是由你決定的,在后面的步驟中,你將會學習如何添加一個 dispatch
函數。
reducer 函數就是你放置狀態邏輯的地方。它接受兩個參數,分別為當前 state 和 action 對象,并且返回的是更新后的 state,如下(在 reducer 中使用 switch 語句 是一種慣例):
function tasksReducer(tasks, action) {switch (action.type) {case 'added': {return [...tasks,{id: action.id,text: action.text,done: false,},];}case 'changed': {return tasks.map((t) => {if (t.id === action.task.id) {return action.task;} else {return t;}});}case 'deleted': {return tasks.filter((t) => t.id !== action.id);}default: {throw Error('未知 action: ' + action.type);}}
}
最后,在組件中導入taskReducer
import { useState } from 'react';
??
import { useReducer } from 'react';const [tasks, setTasks] = useState(initialTasks);
??
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
-
useSelector、useDispatch
useSelector、useDispatch都是react-redux里面的
用法:首先通過reducer給state賦值num
const reducer = (state, action) => {switch (action.type) {case "decrement":return { ...state, num: state.num - 1 };case "increment":return { ...state, num: state.num + 1 };default:return state;}
}
先通過createStore將state存入redux store
const store = createStore(reducer, initialState);
接著通過provider把state傳給子組件
const ComponentUseReactRedux = () => {return (<div><h2>ComponentUseReactRedux</h2><Provider store={store}><ChildComponentUseReactRedux /></Provider></div>)
}
最后在子組件里,useSelector負責讀取和渲染狀態,useDispatch負責觸發狀態更新
- 通過useSelector(選擇器函數)從store全局狀態里提取需要的部分,這里是state里的num。當 dispatch(action) 觸發 reducer 修改狀態后,Store 會通知所有訂閱的組件。
- useSelector 會比較 上一次選擇器返回值 和 新返回值: 如果不同(!==),組件重新渲染。 如果相同,組件不會重新渲染(避免不必要的更新)。
- useDispatch通過distpatch觸發狀態更新,dispatch(action) 是 Redux 中觸發狀態更新的唯一方式
const ChildComponentUseReactRedux = () => {const num = useSelector(state => state.num);const dispatch = useDispatch();return (<div><h3>Using useSelector, useDispatch</h3>Number: {num}//組件調用,dispatch(action),store傳遞state和action//reducer會根據action計算新狀態,store更新狀態并通知組件<button onClick={() => dispatch({ type: "increment" })}>+</button><button onClick={() => dispatch({ type: "decrement" })}>-</button></div>);
}
-
connect
/**
* 簡單寫法
* 將state.products 映射到組件的 props.products
* connect 是一個高階函數,它接收兩個可選參數 mapStateToProps 和 mapDispatchToProps,并返回一個高階組件
*/
export default connect(({ products }) => ({products,
}))(Products);/**
* 更規范的寫法
* 這樣 Products 組件就可以通過 this.props.products 訪問到 Redux store 中的 products 數據
* 這種情況沒有用到mapDispatchToProps,不需要派發actions
*/
const mapStateToProps = (state) => {return {products: state.products,};
};export default connect(mapStateToProps)(Products);/**
* 需要派發action的情況
* mapDispatchToProps 用于將 Action Creator 綁定到組件的 props,這樣組件就可以直接調用它們來派發 Action
*/
import { fetchProducts } from './actions';const mapDispatchToProps = {fetchProducts, // 將 action creator 映射到 props
};export default connect((state) => ({ products: state.products }),mapDispatchToProps
)(Products);