看得明白的react-redux教程

前言

redux核心很簡單。

redux

redux核心

redux 很簡單,總結起來就是一句話:一個帶事件通知的狀態保存者。
看代碼,如裏下面代碼看明白了,直接跳至 插件

const {createStore} = require('redux');

function reducer(state, action) {
    switch (action.type) {
        case 'add':
            return {value: state.value + action.payload}
        case 'dec':
            return {value: state.value - action.payload}
        default:
            return state
    }
}

const store = createStore(reducer, {value: 1})
const subscriber = () => {
    console.log("狀態變化了", store.getState())
};
store.subscribe(subscriber)
store.dispatch({type: 'add', payload: 2})
store.dispatch({type: 'dec', payload: 1})

運行結果

狀態變化了 { value: 3 }
狀態變化了 { value: 2 }

代碼極其簡單,但redux 的所有核心(全部)內容也就在這裏了。只有一個createStore函數,用來創建一個 store 來保存狀態,傳入的參數是一到三個。第一個參數是叫reducer函數, 作用是產生狀態,每二個參數是初始狀態,如果有第三個參數,則是插件。只有第一個參數是必須的。

store對象

就三個方法

  1. getState() , 用來取狀態
  2. dispatch(action),告訴store 一個事件,只要是個對象就行,對象格式無要求,只要reducer認識就行。大多數按官網寫法,寫成 {type:‘someKey’,payload:‘someValue’} ,代表事件名稱,及參數
  3. subscriber(callback) , 當狀態變化後,通知callback , 這個通知極其簡陋,只告訴你變化了,但不告訴你哪裏變化了。

reducer 函數

redux 把生成狀態的函數叫reducer ,接受兩個參數,第一個當前狀態,第二個就是事件內容。 只要注意一點:無論如何要返回一個狀態,比如下面的例子,缺少了default 的處理,就會出錯,因爲少了default處理,在初始化的時候狀態變成undefined

function reducer(state, action) {
    switch (action.type) {
        case 'add':
            return {value: state.value + action.payload}
        case 'dec':
            return {value: state.value + action.payload}
        // default: 這個註釋掉就出錯
        //     return state
    }
}

redux 工作過程

初始化

createStore 調用reducer ,傳入一個參數 :初始狀態,也就是createStore的第二個參數。調用後,得到初始狀態,保存下來。

運行時

三步曲:

用dispatch 發一個事件 => store調用reducer=>發出通知 subscribe

插件

插件就是攔截 store.dispatch 方法,寫法是固定的,下面以一個日誌插件爲例

function logger({dispatch, getState}) {
    //此dispatch 方法是未被攔截的dispatch
    return (next) => {
        // next 是指下一個插件
        return (action) => {
            //需要填寫的內容在此
            console.log('before log', action, getState())
            next(action)
            console.log('after log', getState)
        }
    }
}

const store = createStore(reducer,applyMiddleware(logger))

插件可以形成一個串行調用鏈。上面例子中的 applyMiddleware 方法 由redux提供,參數是可以多個插件,作用就是將多個插件串起來。

redux 其它內容

redux 官網其它內容都是在講編程規範與技巧

取消“魔法”字符串和對象

在上述例子中,寫事件時,都是直接用字符串,比如"add",“dec”,如果多個地方用的話,會容易出錯。編程中將這類容易出錯的字符串叫“魔法”字符串。解決方法就是定義常量。

const TYPE_ADD="TYPE_ADD"
const TYPE_DEC="TYPE_DEC"

同理,產生事件也變成一個方法:

function createAddAction(value){
	return {type:TYPE_ADD,payload:value}
}

combineReducers 合併多個reducer

如果有多個狀態內容,比如user 的狀態,post 的狀態,如果都寫在一個文件裏,會太龐大。所以,用combineReducers這個方法合併

function userReducer(state, action) {
   //省略
}
function postReducer(state, action) {
   //省略
}
const reducer = combineReducers({user: userReducer, post: postReducer})

這樣產生出來的state 就象:

{
   user:{}
   post:{}
}

react-redux 粘合劑

redux 與react 結合時,不用 react-redux 也可工作。狀態改變後,用store.subscriber 通知react重渲染

import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from "redux";

function reducer(state, action) {
   switch (action.type) {
       case 'add':
           return {value: state.value + action.payload}
       case 'dec':
           return {value: state.value - action.payload}
       default:
           return state
   }
}

const store = createStore(reducer, {value: 1})
const subscriber = () => {
     ReactDOM.render(
        <div>
            <div>{store.getState().value}</div>
            <button onClick={() => store.dispatch({type: 'add', payload: 1})}>1</button>
            <button onClick={() => store.dispatch({type: 'dec', payload: 1})}>1</button>
        </div>,
        document.getElementById('root')!
    )
};
store.subscribe(subscriber)

這樣寫,主要的缺陷就是組件與store 耦合緊密,不利於調試與複用,所以就用到react-redux 來解耦與粘合。

Provider 與 connect

主要就兩個內容:Provider 組件與 connect 方法。 Provider 用來提供全局的狀態提供者,connect 將普通Component包裹一下,能夠接收Provider提供的狀態。

import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from "redux";
import {Provider,connect} from 'react-redux'

function reducer(state, action) {
    switch (action.type) {
        case 'add':
            return {value: state.value + action.payload}
        case 'dec':
            return {value: state.value - action.payload}
        default:
            return state
    }
}

const store = createStore(reducer, {value: 1})

//以下開始不一樣

class Counter extends Components{
   render(){
     <div>
       <div>{this.props.value}</div>
       <button onClick={this.props.addMethod}>1</button>
       <button onClick={this.props.decMethod}>1</button>
     </div>      
   }
}
function mapStateToProps(state){
	return {
	  value:state.value
	}
}
function mapDispatchToProps(dispatch){
    return {
      addMethod:()=>dispatch({type: 'add', payload: 1})
      decMethod:()=>dispatch({type: 'dec', payload: 1})
    }
}
const WrapedCounter=connect(mapStateToProps,mapDispatchToProps)(Counter)

ReactDOM.render(
    (
       <Provider store={store}>
           <WrapedCounter/>
       </Provider>
     ),
     document.getElementById('root')
)       

connect 方法需要兩個函數參數,一個是mapStateToProps ,把store中的狀態映射到組件的屬性。一個是mapDispatchToProps,把發送事件的方法映射成組件的屬性。

總結

謝謝閱讀

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章