Redux 是 JavaScript 狀態容器, 提供可預測化的狀態管理。javascript
那什麼是能夠預測化,個人理解就是根據一個固定的輸入,必然會獲得一個固定的結果。css
redux是專門爲react開發的,但並非只能用於react,能夠用於任何界面庫。html
隨着單頁面應用的普及,web app內部須要管理的狀態愈來愈多,這些狀態可能來自服務器端,用戶輸入的數據,用戶交互數據,當前UI狀態,本地的緩存數據等等。如何可以有條理的管理這些數據,成爲前端開發中一個難題。前端
使用redux的程序,全部的state都存儲在一個單一的數據源store內部,相似一個巨大的對象樹。java
state是隻讀的,能改變state的惟一方式是經過觸發action來修改react
爲了描述 action 如何改變 state tree , 你須要編寫 reducers。web
reducers是一些純函數,接口當前state和action。只須要根據action,返回對應的state。並且必需要有返回。sql
一個函數的返回結果只依賴於它的參數,而且在執行過程裏面沒有反作用,咱們就把這個函數叫作純函數typescript
顧名思義,action就是動做,也就是經過動做來修改state的值。也是修改store的惟一途徑。express
action本質上就是一個普通js對象,咱們約定這個對象必須有一個字段type,來表示咱們的動做名稱。通常咱們會使用一個常量來表示type對應的值。
此外,咱們還會把但願state變成什麼樣子的對應的值經過action傳進來,那麼這裏action可能會相似這樣子的
{
type: 'TOGGLE_TODO', index: 5 }
Action 只是描述了有事情發生了這件事實,但並無說明要作哪些改變,這正是reducer須要作的事情。
Reducer做爲純函數,內部不建議使用任何有反作用的操做,好比操做外部的變量,任何致使相同輸入但輸出卻不一致的操做。
若是咱們的reducer比較多,比較複雜,咱們不能把全部的邏輯都放到一個reducer裏面去處理,這個時候咱們就須要拆分reducer。
幸虧,redux提供了一個api就是combineReducers Api。
store是redux應用的惟一數據源,咱們調用createStore Api建立store。
第一步搭建開發環境,這裏不介紹了,參考上一篇文章 手把手教會使用react開發日曆組件,搭建環境部分
搭建好環境切換到目錄下面
npm install redux --save
把index.tsx修改成之下代碼。
import { createStore, combineReducers, applyMiddleware } from 'redux' var simpleReducer = function(state = {}, action) { return { user: { name: 'redux' } } } var store = createStore(simpleReducer) console.log(store.getState())
咱們看到控制檯打印出來的一個包含user信息的這麼一個對象。
咱們使用到了幾個api? createStore
建立store,store.getState()
獲取store,也就是惟一數據源的根節點。
上文咱們也講過,action的狀況可能會比較多,redux也提供了combineReducers
Api。若是咱們有多個reducer,咱們就可使用起來了。
那咱們建立多個reducer測試一下,代碼以下:
import { createStore, combineReducers, applyMiddleware } from 'redux' function user(state = {name: 'redux'}, action) { switch (action.type) { case 'CHANGE_NAME': return { ...state, name: action.name } } return state } function project(state = {name: 'min-react'}, action) { switch (action.type) { case 'CHANGE_NAME': return { ...state, name: action.name } } return state } var rootReducer = combineReducers({ user, project }) var store = createStore(rootReducer) console.log(store.getState())
如咱們所預料同樣,咱們獲得擁有兩個字段的根store。
第一步咱們把html改形成這個樣子,新增了一點標籤
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style type="text/css"> * { margin: 0; padding: 0; } </style> </head> <body> <div id="userName"></div> <input id="userNameInput"/><button id="userNameButton">更改userName</button> <script src="./dist/main.js"></script> </body> </html>
第二步,修改index.tsx,以下
import { createStore, combineReducers, applyMiddleware } from 'redux' import { func } from 'prop-types' function user(state = {name: 'redux'}, action) { switch (action.type) { case 'CHANGE_USER_NAME': return { ...state, name: action.name } } return state } function project(state = {name: 'min-react'}, action) { switch (action.type) { case 'CHANGE_PROJECT_NAME': return { ...state, name: action.name } } return state } var rootReducer = combineReducers({ user, project }) var store = createStore(rootReducer) function render(state = store.getState()) { var $userName = document.getElementById('userName') $userName.innerHTML = state.user.name } render() console.log(store.getState())
咱們看到頁面正確的顯示了咱們user的名稱。下一步咱們須要作的就是經過用戶的操做,改變store的值,進而觸發view的更新。
因而咱們新增了這塊代碼:
store.subscribe(function() { render() }) // 綁定用戶事件 var $userNameInput = document.getElementById('userNameInput') var userNameButton = document.getElementById('userNameButton') userNameButton.onclick = function() { var value = $userNameInput.value store.dispatch({ type: 'CHANGE_USER_NAME', name: value }) }
咱們看到保存以後,當咱們輸入值以後,點擊更改,頁面的值隨着改變。
可是控制檯報了一個錯誤,TS2339: Property 'value' does not exist on type 'HTMLElement'.
,這是因爲typescript強類型校驗沒經過致使的。只要加這段代碼就行了
var $userNameInput = document.getElementById('userNameInput') as HTMLInputElement
看到了吧,redux就是這麼簡單。
其餘全部上層應用,都是在此基礎上開發的,因此開發一個redux應用的步驟就是
結合react,其餘view類庫,開發步驟莫不如此。
咱們也看到了,咱們的reducer只能作同步應用,若是咱們須要在reducer,作一些延遲操做,可怎麼辦
社區已經有成熟的類庫作這件事件
npm install redux-thunk --save
redux自己已經提升了很好的擴展機制,就是中間件。這點很相似express的中間件。
//引入新的類庫 import { createStore, combineReducers, applyMiddleware, compose } from 'redux' import thunk from 'redux-thunk' ... //store部分作以下修改 const finalCreateStore = compose(applyMiddleware(thunk))(createStore) const store = finalCreateStore(rootReducer, {})
redux-thunk的做用就是讓dispatch方法不只僅只接收action對象,還能夠包含一個方法。咱們能夠在這個方法內部去調用異步代碼
咱們把dom事件部分作了以下改造
userNameButton.onclick = function() { var value = $userNameInput.value store.dispatch<any>(function(dispatch, getState) { setTimeout(() => { dispatch({ type: 'CHANGE_USER_NAME', name: value }) }, 2000) }) }
能夠看到頁面元素確實在2s以後發生了變化,實際業務中啊,咱們這裏能夠作一些異步操做。
至於redux原理,以及源碼和中間件的源碼講解能夠參照個人另一篇文章 閱讀redux源碼