注意:讀懂本文須要具有redux基礎知識,css
註明:本文旨在說明如何在實際項目中快速使用react-redux,限於篇幅,本文對具體的原理並未作分析,請參考redux官網html
我一直覺得我寫了一篇關於react-redux的文章,昨天在翻個人博客時才發現沒有。vue
前幾天寫了vue的狀態管理 vuex,Vue中的狀態管理器 - Vuex,講了vue中狀態管理的用法,總體感受是很簡單的,react
狀態和路由都是官方本身整合的,不論是文檔仍是用來都比較順滑,而redux的門檻要比vuex高很多。webpack
題外話:爲何vue這麼好用你還要搞react呢,個人感受是,vue就像qq,react就像facebook,雖然均可以社交,可是味道不同,各有所好,web
另外react-native能夠作app目前vue不能,若是外包我選擇vue(快),要作一個長期的網站我會用react。vuex
說了一大堆,開始正文:npm
前面我寫了一篇文章:快速搭建一個vue開發環境,用到了vue官方的腳手架,一樣,react官方也有一個,相似功能的:create-react-app。redux
補充一點額外常識:vue中路由和狀態管理都是官方整合的,屬於官方庫,而在react中,只有react! 路由和狀態管理由第三方庫提供。react-native
全局安裝 create-react-app
npm install -g create-react-app
建立一個 test的項目
create-react-app test
經過這句話搭建的環境已經能夠正常跑起來了,集成了webpack,和熱加載,更以前vue搭建那個環境同樣,不過沒有路由和狀態管理,
這一篇我只講react+狀態管理,路由之後單獨寫,或者可能不用寫了,路由是很簡單的,看一下就知道怎麼用。
在項目目錄下執行 npm run start 項目就跑起來了,嘗試修改一下 src/App.js,頁面輝自動從新加載。
安裝redux
#說明:redux是一個通用狀態管理庫,能夠用於任何現代化框架,好比vue,react,angular等,不侷限於react,均可以直接使用redux。
在react中, 你除了能夠直接使用redux外,redux針對react推出了react-redux綁定,使用起來更規範和順滑。
本文要講解的是 react-redux的用法,並假設你已經有了redux的基礎概念。
npm install redux react-redux redux-thunk --save // redux是核心,react-redux是react的綁定,redux-thunk是異步中間件
如今假設咱們網頁上有三個數據須要讓redux來管理,分別是 用戶的信息name,sex和系統的背景顏色bgcolor。
在src下新建actions目錄,在目錄下面新建userAction.js文件,
在src下新建reducers目錄,userReducer.js,rootReducer.js兩個文件,
在src下新建store.js 文件
整個流程是這樣的, view層獲取store中的數據渲染頁面,當在view中須要變動store中的數據的時候,就發送一個action過去,接收這個action的不是
store,而是reducer,reducer更改數據後更新state,在這個更新過程當中會觸發view層的渲染邏輯,而後view層從新渲染。
在一個頁面中會有不少操做要更改store中的數據,每個操做就對應一個action。
userAction.js 代碼:
export const nameAction = (name) =>{ return { type: 'name', payload: name } } export const sexAction = () =>(dispatch)=>{ setTimeout(()=>{ dispatch({ type: 'sex', payload: '男' }) },2000) }
第一個action是用來更更名字的,第二個action是用來更改性別的,這兩個action看起來有差別,第一個action返回一個對象,而第二個action
是一個高階函數,返回了一個函數。
第一個action是普通的操做,發送這個action後store中的數據立馬就會更新,而第二個action是用來處理異步數據的,能夠把這個地方的setTimeout替換成一個
網絡請求,好比我發送了一個更新資料的action,可是這個更新要從服務器動態獲取,那麼就只能用第二種方式,用第二種方式的前提是加載了thunk中間件,不然第二種方式會報錯。
userReducer.js 代碼:
let init = { 'name': '張三', 'sex': '女' } export default (state=init,action)=>{ switch(action.type) { case 'name': state.name = action.payload return {...state} case 'sex': state.sex= action.payload return {...state} // 在沒有匹配的狀況下必定要返回舊的state default: return state } }
reducer是純函數,第一個參數是當前state,第二個參數是接收的action,reducer經過action的type來判斷要處理的邏輯,
注意,reducer必定要返回新的state才能觸發view從新渲染,因此這裏返回的 {...state} 就是一個新對象,最後若是沒有匹配到
對應action,就返回舊的state。
rootReducer 代碼:
import {combineReducers} from 'redux' // import systemReducer from './systemReducer' import userReducer from './userReducer' // 包含全部的計算函數 export default combineReducers({ // system: systemReducer, user: userReducer })
前面一個reducer是處理用戶板塊action的,一個網站有不少板塊,好比系統設置,若是把全部的處理都放到一個reducer中,那麼這個函數會很是龐大,
因此在文件組織上咱們把 reducer按照類型拆分紅多個reducer,最後用redux提供的combineReducer函數進行合併,rootReducer就是幹這事的
store.js 代碼:
import {createStore,applyMiddleware} from 'redux' import thunk from 'redux-thunk' import rootReducer from './reducers/rootReducer' export default function configureStore() { return createStore( rootReducer, applyMiddleware(thunk) ) }
這段代碼是配置store的,經過createStore函數來建立一個store,第一個參數是 reducer,第二個參數是加載中間件,這個地方加載了thunk中間件,是redux官方提供的,
專門用來處理異步action,上面的userAction中設置sex時舊採用了異步的方式。
index.js 代碼:
import React from 'react'; import ReactDOM from 'react-dom'; import {Provider} from 'react-redux'; import configureStore from './store'; import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; ReactDOM.render( <Provider store={configureStore()}> <App /> </Provider>, document.getElementById('root')); serviceWorker.unregister();
前面已經搞定了action,reducer,store,這個文件就是應用redux道項目中,主要關注 Provider和 store兩個地方,Provider時redux提供給react的包裝器,
store屬性用來注入狀態管理。這個地方就不解釋內部原理了,只要知道這麼用就行。
App.js 代碼:
import React, { Component } from 'react'; import {connect} from 'react-redux'; import {nameAction,sexAction} from './actions/userAction'; // import {bgcolorAction} from './actions/systemAction'; // import logo from './logo.svg'; import './App.css'; class App extends Component { componentDidMount() {} render() { return ( <div className="App" > <header className="App-header" > {/* <p style={{color:this.props.system.backgroundColor}}> */} <p> name:{this.props.user.name} <br/> sex:{this.props.user.sex} <br/> </p> <button onClick={()=>this.props.name('利薩')}>更更名字</button> <button onClick={()=>this.props.sex()}>更改性別</button> {/* <button onClick={()=>this.props.bgcolor()}>更改顏色</button> */} <pre> { JSON.stringify(this.props) } </pre> </header> </div> ); } } const mapStateToProps = state => ({ ...state }) const mapDispatchToProps = dispatch=>({ name: (name)=>dispatch(nameAction(name)), sex: ()=>dispatch(sexAction()) // bgcolor: ()=>dispatch(bgcolorAction()) }) export default connect(mapStateToProps,mapDispatchToProps)(App);
這個地方關注 connect,mapStateToProps,mapDispatchToProps,
connect是redux提供的鏈接器,任何要使用redux這一套的view就必須使用connect鏈接,mapStateToProps是將store中的數據複製到
本view的props中,並且是實時同步的,
mapDispatchToProps 是將actioni複製到本view的props中,讓view擁有發送action的機會。