徹底讓你理解redux

  • 2019 年 10 月 8 日
  • 筆記

臨危受命,一周各種熬夜突擊redux,好吧,剛開始真的各種香菇藍瘦,前天還熬到凌晨五點多,寫了點demo,有點感覺,一點點記錄下。這裡簡單介紹下Redux以及其與react結合的使用方法

我們為什麼需要Redux,什麼是Redux

state

state才是真正的前端資料庫,它存儲著這個應用所有需要的數據。 這裡拿一個簡單的例子說明下,為什麼說簡單的例子呢,因為簡單到不應該使用redux。。。 運行效果如圖(學習redux這個例子被介紹爛了):

項目的運行效果大家應該能猜到哈,猜不到的clone下項目几几運行下:-) 所以這裡的如說問你,這個應用應該存在數據里什麼數據呢?對的,就一個count,所以資料庫就存一個count就可以了,同理,這個應用的state其實就醬紫:

注釋錯了,是count值為3 .╥﹏╥…

這裡展示的不是很明顯,可以拿我們下一篇部落格的demo來查看下它的 state

所以說道這裡,大家對於state就已經明白了,需要說明一下的是,一個應用只應該有一個state。對,不管多大,就一個!

action

既然這些state已經有了,那麼我們是如何實現管理這些state中的數據的呢,當然,這裡就要說到action了。 什麼是action?E:action,中:動作。 是的,就是這麼簡單。。。 只有當某一個動作發生的時候才能夠觸發這個state去改變,那麼,觸發state變化的原因那麼多,比如這裡的我們的點擊事件,還有網路請求,頁面進入,滑鼠移入。。。所以action的出現,就是為了把這些操作所產生或者改變的數據從應用傳到store中的有效載荷。 需要說明的是,action是state的唯一來源。 action也沒有什麼神秘,本質上就是一個JavaScript對象,但是約定的包含type屬性(你總得告訴你這個action是啥嘛),可以理解成每個人都要有名字一般。除了type屬性,別的屬性,都可以DIY~ 那麼這麼多action一個個手動創建必然不現實,一般我們會寫好action creator,即action的創建函數。調用action creator,給你返回一個action。

比如這個counter應用,我們就有兩個action,一個decrement,一個increment。 所以這裡的action creator寫成如下:

export function decrement() {     return{         type:DECREMENT_COUNTER     }  }export function increment(){     return{         type:INCREMENT_COUNTER     }  }

好吧,藏也藏不住了,你也發現了這裡有另外兩個按鈕,幹嘛的?奇數是點擊+1、延遲+1,對應的action creator:

export function incrementIfOdd(){     return(dispatch,getState)=>{         const {counter} = getState();         if(counter%2==0) {             return;         }         dispatch(increment());     }  }export function incrementAsync() {     return dispatch => {         setTimeout(() => {             dispatch(increment());         }, 1000);     };  }

好吧,又被你發現了,不是說action creator返回的是一個action對象么,你這返回的什麼鬼?這裡留個疑惑好吧,簡單的解釋, 為什麼可以這麼用呢,因為我用了中間件呀~(後續會介紹)

為了減少樣板程式碼,我們使用單獨的模組或文件來定義 action type 常量

 export const INCREMENT_COUNTER = 'INCREMENT_COUNTER';  export const DECREMENT_COUNTER = 'DECREMENT_COUNTER';

這麼做不是必須的,在大型應用中把它們顯式地定義成常量還是利大於弊的。

如果我都快把action說完了你還是不懂action是什麼的話,你就理解成,一個可能!改變state的動作包裝。

reducer

既然這個可能改變state的動作已經包裝好了,那麼我們怎麼去判斷並且對state做相應的改變呢?對,這就是reducer乾的事情了。 從一開始我們就說明下一個概念:

reducer決定了state的最終格式。

reducer是一個純函數,也就是說,只要傳入參數相同,返回計算得到的下一個 state 就一定相同。沒有特殊情況、沒有副作用,沒有 API 請求、沒有變數修改,單純執行計算。

reducer對傳入的action進行判斷,然後返回一個通過判斷後的state,這就是reducer的全部職責。 從程式碼可以簡單地看出:

import {INCREMENT_COUNTER,DECREMENT_COUNTER} from '../actions';export default function counter(state = 0,action) {     switch (action.type){         case INCREMENT_COUNTER:             return state+1;         case DECREMENT_COUNTER:             return state-1;         default:             return state;     }  }

這裡我們就是對增和減兩個之前在action定義好的常量做了處理。

對於一個比較大一點的應用來說,我們是需要將reducer拆分的,最後通過redux提供的combineReducers方法組合到一起。 如此項目上的:

const rootReducer = combineReducers({     counter  });export default rootReducer;

雖然這裡我們就一個counter (ಥ_ಥ) 但是道理你懂得。

這裡你要明白:每個 reducer 只負責管理全局 state 中它負責的一部分。每個 reducer 的 state 參數都不同,分別對應它管理的那部分 state 數據。 combineReducers() 所做的只是生成一個函數,這個函數來調用你的一系列 reducer,每個 reducer 根據它們的 key 來篩選出 state 中的一部分數據並處理, 然後這個生成的函數再將所有 reducer 的結果合併成一個大的對象。

store

store是對之前說到一個聯繫和管理。具有如下職責

  • 維持應用的 state;
  • 提供 getState() 方法獲取 state
  • 提供 dispatch(action) 方法更新 state;
  • 通過 subscribe(listener) 註冊監聽器;
  • 通過 subscribe(listener) 返回的函數註銷監聽器。

後面兩個不怎麼用哈~

再次強調一下 Redux 應用只有一個單一的 store。當需要拆分數據處理邏輯時,你應該使用 reducer 組合 而不是創建多個 store。 store的創建通過redux的createStore方法創建,這個方法還需要傳入reducer,很容易理解:畢竟我需要dispatch一個action來改變state嘛。 應用一般會有一個初始化的state,所以可選為第二個參數,這個參數通常是有服務端提供的,傳說中的Universal渲染。後面會說。。。 第三個參數一般是需要使用的中間件,通過applyMiddleware傳入。

說了這麼多,action,store,action creator,reducer關係就是這麼如下的簡單明了:

接合react-redux的使用

說到react-redux,必然想到react和redux,是的,react-redux正是redux和react的橋樑工具。

react-redux將組建分成了兩大類,UI組建和容器組建。 簡單的說,UI組建負責美的呈現,容器組件負責來幫你盛著,給你"力量"

好吧,官方點: UI 組件有以下幾個特徵:

  • 只負責 UI 的呈現,不帶有任何業務邏輯
  • 沒有狀態(即不使用this.state這個變數)
  • 所有數據都由參數(this.props)提供
  • 不使用任何 Redux 的 API

如:

export default class Counter extends Component{     render(){         const {counter,increment,decrement,incrementIfOdd,incrementAsync} = this.props;         return(             <p>                 Clicked:{counter} times                 {'  '}                 <button onClick={increment}>+</button>                 {'  '}                 <button onClick={decrement}>-</button>                 {'  '}                 <button onClick={incrementIfOdd}>increment if Odd</button>                 {'  '}                 <button onClick={incrementAsync}>increment async</button>             </p>         )     }  }

容器組件特性則恰恰相反:

  • 負責管理數據和業務邏輯,不負責 UI 的呈現
  • 帶有內部狀態
  • 使用 Redux 的 API

如:

class App extends Component{     render(){         const {counter,increment,decrement,incrementIfOdd,incrementAsync} = this.props;         return(             <Counter                 counter={counter}                 increment={increment}                 decrement={decrement}                 incrementIfOdd={incrementIfOdd}                 incrementAsync={incrementAsync}/>         )     }  }App.propTypes = {     counter:PropTypes.number.isRequired,     increment:PropTypes.func.isRequired,     decrement:PropTypes.func.isRequired,     incrementIfOdd:PropTypes.func.isRequired,     incrementAsync:PropTypes.func.isRequired  };export default connect(     state=>({         counter:state.counter     }),     ActionCreators  )(App);

說到這裡大家應該都懂,那麼問題來了,redux和react如何產生點關係呢??不難想到,如果產生關係肯定只要跟容器組件產生關係就可以了,畢竟他是react這些組件的老祖宗。 那麼如何產生關係呢??對的,就是上面程式碼里的,react-redux中的connect方法。

connect方法接受兩個參數:mapStateToProps和mapDispatchToProps。它們定義了 UI 組件的業務邏輯。前者負責輸入邏輯,即將state映射到 UI 組件的參數(props), 後者負責輸出邏輯,即將用戶對 UI 組件的操作映射成 Action。

比如這樣:

export default connect(   state => ({ counter: state.counter }),   ActionCreators  )(Counter);

因為作為組件,我們只要能拿到值,能發出改變值得action就可以了,所以mapStateToProps和mapDispatchToProps正是滿足這個需求的。

當時對這個connect也是好一頓理解

這麼通俗的說你該明白了吧

可以吧所有的組件想像成裝在一個罐子里,這個罐子使用container做的,然後這個罐子的唯一的口就是裡面的東西想要去改變的唯一途徑。 說白了,這個口,就是connect,而redux中的所有的組件都是在罐子外面的。

reducer是改變state的,state就可以可以理解成組件的糧食,需要的時候redux就把糧食通過dispatch投入到罐子里。 那麼我怎麼知道你需要呢?所以我們之間得有個約束,你喊一聲餓了,我就知道你要吃飯了,你喊一聲渴了,我就知道你要喝水了。 這些動作,就是你發出的action,喊得詞語,餓了,渴了,就是action.type,然後redux拿給軍事reducer解讀下,到底給罐子里投入什麼。

不知道這麼通俗的解釋有沒有說明白 (T_T)

所以這樣看來,組件通過container包裝以後和redux就可以說是完全隔絕了,組件就是做組件的事情,redux就是做redux的事情。中間的樞紐是connect。 這也就說明了,redux並不是只服務我們react噠~也即是我這一套邏輯在罐子外面,罐子裡面是什麼其實我並不是很在意。。。只要我們預定好action和state就可以了。

所以。。。redux也並沒有特別神秘的地方。 當然,這裡只是簡單的說了下redux的簡單概念,並沒有特別深入的講解。 關於redux的非同步操作,以及在服務端的運行(node),universal渲染,結合react-router的使用等等等的功能,咱再慢慢了解慢慢研究慢慢總結哈~

ヾ(^▽^ヾ)