本章主要講解redux在React項目中的應用。Redux官方文檔。
一、基本概念
Action
action簡單理解就是對象字面量。功能上來說就是把數據從應用傳到store的有效載荷,和Vue中Mutation提交載荷類似。
有action創造函數,是一個生成action的方法。下面就是一個簡單的同步actin:
export function setGlobalState(data: {}) { return {type: 'SET_GLOBAL_STATE', data} }
action生成后,store並沒有生成或者發生變化,只是說明我要更新store了。怎么更新需要通過reducer函數來處理。
Reducer
reducer本質上是純函數,接受oldState、action兩個參數,返回newState。reducer告訴開發系統是怎么更新store的。
function global(state: Global, action: any) { switch(action.type) { case 'SET_GLOBAL_STATE': return action.data default: return InitGlobalState } }
reducer支持組合和拆分,可以根據項目業務合理進行組織。
Store
store就是項目中的數據流,將action和reducer聯系起來。Redux應用只有一個store,起職責如下:
- 維持應用的 state;
- 提供
getState()
方法獲取 state; - 提供
dispatch(action)
方法更新 state; - 通過
subscribe(listener)
注冊監聽器; - 通過
subscribe(listener)
返回的函數注銷監聽器。
下面我們初始化一個store
import { createStore } from 'redux' // 初始化store const Store = createStore( rootReducer, )
二、React中使用Redux
redux是一種狀態管理通用實現方案,可以應用在許多地方,並不受庫的限制。react-redux正是兩者搭配起來實現的通用庫。
yarn add react-redux
Provider全局注入store
然后通過react-redux提供的Provider組件實現store注入。
import Store from './store' import { Provider } from 'react-redux' ReactDOM.render( <Provider store={Store}> <App/> </Provider>, document.getElementById('root') as HTMLElement //類型斷言 );
這樣項目中的所有組件都能訪問到store中state,並且通過dispatch來更新state。總之,store在項目中是單獨存在的一個整體,作為數據源存在。而項目外部可以獲取state,改變store中state的唯一方法就是dispatch(action)。
組件中使用store
下面我們一todo.tsx組件為例,介紹組件中是如何獲取、更新state的。
import { connect } from 'react-redux' const mapStateToProps = (state:InitState) => { return { todos: state.todos } } const mapDispatchToProps = (dispatch:Dis) => { return { onAddTodo(value: string) { dispatch(addTodo(value)) } } }
Provider組件將store注入到組件中的本質,還是將store通過組件的props關聯到每個組件上。接下來我們將兩個map函數綁定到組件的屬性上。
interface Props { todos: [] onAddTodo:(value: string) => void } interface State { value: string } class Todo extends React.Component<Props, State> { constructor(props: Props) { super(props) this.state = { value: '' } } handleAddTodo = () => { this.props.onAddTodo(this.state.value) this.setState({ value: '' }) } handleInputChange = (e: any) => { const { value } = e.target this.setState({ value }) } render () { return( <div> <Input value={this.state.value} placeholder="請輸入清單項" onChange={this.handleInputChange}/> <Button onClick={this.handleAddTodo}>添加</Button> <ul> {this.props.todos.map((todo: any) => ( <li>{todo.text}</li> ))} </ul> </div> ) } } export default connect( mapStateToProps, mapDispatchToProps )(Todo)
這樣我們就實現了在組件內部獲取state,並且通過界面交互可以動態新增待辦清單,更新store,最后實時渲染頁面的功能。
三、redux高級用法
組合拆分reducer
之前有講到可以拆分reducer,做到單一功能模塊對應一個reducer。類似於Vuex中將store分成多個子modules。
redux提供的combineReducers方法即可接受多個reducer。
import { createStore, combineReducers } from 'redux' import Todo from './todo/reducers' import Ware from './ware/reducers' const rootReducer = combineReducers( Object.assign({}, Todo, Ware) )
然后將rootReducer傳入createStore中即可。
action中間件
中間件MiddleWare可以在我們進行disatch時,更加細粒化的跟蹤store的變化,並且允許我們建立異步action。
下面我們來看react-thunk中間件的作用,使用middleWare后,action創建函數除了返回action對象外,還能返回函數。這個函數會被react-thunk執行,在函數內部能執行更多操作,執行異步請求、dispatch action。
yarn add react-thunk
下面我們來修改下store/index.tsx文件:
import thunkMiddleware from 'redux-thunk' import { createStore, applyMiddleware, combineReducers } from 'redux' const Store = createStore( rootReducer, applyMiddleware( thunkMiddleware, ) )
現在我們的action創造函數中就能像下面這樣寫:
export function addTodo(text: string) { return { type: 'ADD_TODO', text } } export function asyncAddTodo(text: string) { return (dispatch:Dis, getState: Get) => { if (checkStoreTodo(getState(),text)) { // 在 thunk 里 dispatch 另一個 thunk! return dispatch(addTodo(text)) } else { // 告訴調用代碼不需要再等待。 return Promise.resolve() } } }
還有react-logger提供的中間件,在本地開發時在進行dispatch時,能打印改動日志。不僅如此,我們還可以自定義middleWare,用來實現自己想要的邏輯效果。
現在redux在react中一些基本用法都介紹完畢了,react中的一些第三方庫基本和TypeScript結合的很好,可能有時候回調函數的參數不知道如何進行類型定義。有個d.ts文件則能在開發時進行智能提示,這個文件主要是在ts文件以js文件發布后,用來標記js文件里面對應的類型。
TypeScript官方文檔有興趣的朋友可以看看。
題外話,redux使用起來有點復雜。安利另外一個狀態管理方案MobX,並且在react得到很好的實現,真正做到了開箱即用,代碼寫到哪里就在哪里使用!
這是一個系列文章:
從零搭建React+TypeScript的后台項目(一)--構建基礎React+TypeScript項目
從零搭建React+TypeScript的后台項目(二)--后台router實現方案
從零搭建React+TypeScript的后台項目(三)--Redux基本配置