再厲害的魔術也比不上真正的redux

Meteor發表於2018-06-06

why redux?

  • 隨著 JavaScript 單頁應用開發日趨複雜,管理不斷變化的 state 非常困難
  • Redux的出現就是為了解決state裡的資料問題
  • 在React中,資料在元件中是單向流動的
  • 資料從一個方向父元件流向子元件(通過props),由於這個特徵,兩個非父子關係的元件(或者稱作兄弟元件)之間的通訊比較麻煩

再厲害的魔術也比不上真正的redux

what is redux?

工作流

再厲害的魔術也比不上真正的redux

設計思想

  • Redux是將整個應用狀態儲存到到一個地方,稱為store
  • store裡面儲存一棵狀態樹(state tree)
  • 元件可以派發(dispatch)行為(action)給store,而不是直接通知其它元件
  • 其它元件可以通過訂閱store中的狀態(state)來重新整理自己的檢視.

三大原則

  • 整個應用的 state 被儲存在一棵 object tree 中,並且這個 object tree 只存在於唯一一個 store 中 State 是隻讀的,惟一改變 state 的方法就是觸發 action,
  • action是一個用於描述已發生事件的普通物件 使用純函式來執行修改,為了描述action如何改變state tree ,你需要編寫 reducers
  • 單一資料來源的設計讓React的元件之間的通訊更加方便,同時也便於狀態的統一管理

how redux?

1.安裝

npm i redux -S
複製程式碼

2.簡單例子

1.引入

import {createStore} from 'redux';
//createStore 用來建立狀態倉庫
複製程式碼

2.建立state

let initState = {
    title: 'star'
}
複製程式碼

3.建立reducer

const CHANGETITLE = 'CHANGETITLE'; //action-todos
function reducer(state= initState, action){
     switch(action.type){
         case CHANGETITLE: 
             return state.title = action.title;
     }
 }
複製程式碼

4.建立倉庫

let store = createStore(reducer);
複製程式碼

5.觸發dispatch中傳入action

store.dispatch({type: CHANGETITLE, title: 'xingxing'})
複製程式碼

完整程式碼

import {createStore} from 'redux';
const CHANGETITLE = 'CHANGETITLE';
function reducer(state= initState, action){
    switch(action.type){
        case CHANGETITLE: 
            state.title = action.title;
    }
    console.log(state);
}
let store = createStore(reducer);
store.subscribe(()=>{                    //訂閱事件,在dispatch時觸發
    console.log('render');
})
store.dispatch({type: CHANGETITLE, title: 'xingxing'})
複製程式碼

複雜些例子

在真實開發中需要開闢一個檔案空間來管理倉庫

再厲害的魔術也比不上真正的redux

  • 檔案結構化
  • 多reducer,合併reducer

1.actions

action-type.js
//action-type衡量,通過引入使用,減少拼寫錯誤引發的問題
export const INCREMENT = 'INCREMENT'
export const DECREMNET = 'DECREMNET'
複製程式碼
actions/count.js
import * as types from "../action-types"
//用於生成action
let counter = {
    add(n){
         return {type: types.INCREMENT, count: n}
    }
}
export default counter
複製程式碼

2.reducers

reducers/count.js
import {INCREMENT,DECREMENT} from '../actions/action-type'
let initState = {
    count: 0
}
function reducer(state = initState,action){
    switch(action.type){
        case INCREMENT:
             state.count = state.count + action.number;
            break;
        case DECREMENT:
            state.count = state.count - action.number;
            break;
    }
    return state
}
export default reducer
複製程式碼
合併reducer
reducers/index.js
import todos from './todo';
import count from './count';
import {combineReducers} from 'redux'
let reducers = combineReducers({
    todos,
    count
})
export default reducers;
複製程式碼

store/index.js 初始化倉庫

import {INCEMENT,DECREMENT} from './actions/action-type';
import {createStore} from 'redux';
import reducers from './reducers'

export default createStore(reducers);
複製程式碼

how is redux work?

  • redux的資料來源是建立reducer時,傳進去的initState。
  • 為了避免state被隨意篡改,redux通過dispatch reducer來更改資料。
  • redux可以通過subscribe訂閱狀態修改事件
//簡單實現
function createStore(reducer){
    let state;
    let listeners = [];
    function subscribe(listener){
        listeners.push(listener);
        return ()=> listeners = listeners.filter(fn=>fn!==listener);
    }
    function dispatch(action){
        listeners.forEach(listener=>listener());
        reducer(state, action)
    }
    dispatch({})
    function getState(){
        return state;
    }
    return {subscribe, dispatch, getState}
}
複製程式碼

合併reducer

function combineReducers(reducers){
    return (state={},action)=>{
        let newState = {};
        for(let key in reducers){
            let s = reducers[key](state[key],action);
            newState[key] = s;
        }
        return newState;
    }
}
複製程式碼

完整程式碼

function createStore(reducer){
    let state;
    let listeners = [];

    function subscribe(listener){
        listeners.push(listener);
        return ()=>{
            listeners = listeners.filter(l=> l!==listener)
        }
    }
    dispatch({})
    function dispatch(action){
        state = reducer(state,action);
        listeners.forEach(l=>l());
    }
    function getState(){
        return state;
    }
    return {subscribe,dispatch,getState};
}

function combineReducers(reducers){
    return (state={},action)=>{
        let newState = {};
        for(let key in reducers){
            let s = reducers[key](state[key],action);  
            newState[key] = s;
        }
        return newState;
    }
}

export {createStore,combineReducers}
複製程式碼

最近在研究redux,歡迎指出問題。後續更新react-redux全家桶系列研究

相關文章