Fish Redux中的Dispatch是怎麼實現的?

閒魚技術發表於2019-04-16

前言

開源地址:

我們在使用fish-redux構建應用的時候,介面程式碼(view)和事件的處理邏輯(reducer,effect)是完全解耦的,介面需要處理事件的時候將action分發給對應的事件處理邏輯去進行處理,而這個分發的過程就是下面要講的dispatch, 透過本篇的內容,你可以更深刻的理解一個action是如何一步步去進行分發的。

從example開始

為了更好的理解action的dispatch過程,我們就先以todolistpage中一條todo條目的勾選事件為例,來看點選後事件的傳遞過程,透過斷點debug我們很容易就能夠發現點選時候發生的一切,具體過程如下:

  1. 使用者點選勾選框,GestureDetector的onTap會被回撥

  2. 透過buildView傳入的dispatch函式對doneAction進行分發,發現todo_component的effect中無法處理此doneAction,所以將其交給pageStore的dispatch繼續進行分發

  3. pageStore的dispatch會將action交給reducer進行處理,故doneAction對應的_markDone會被執行,對state進行clone,並修改clone後的state的狀態,然後將這個全新的state返回

  4. 然後pageStore的dispatch會通知所有的listeners,其中負責介面重繪的_viewUpdater發現state發生變化,通知介面進行重繪更新

Dispatch實現分析

Dispatch在fish-redux中的定義如下

typedef Dispatch = void Function(Action action);

本質上就是一個action的處理函式,接受一個action,然後對action進行分發。

下面我門透過原始碼來進行詳細的分析。

01    component中的dispatch

buildView函式傳入的dispatch是對應的component的mainCtx中的dispatch, _mainCtx和componet的關係如下 component->ComponentWidget->ComponentState->_mainCtx->_dispatch而 _mainCtx的初始化則是透過componet的createContext方法來建立的,順著方法下去我們看到了dispatch的初始化

  1. // redux_component/context.dart DefaultContext初始化方法


  2.  DefaultContext({

  3.    @required this.factors,

  4.    @required this.store,

  5.    @required BuildContext buildContext,

  6.    @required this.getState,

  7.  })  : assert(factors != null),

  8.        assert(store != null),

  9.        assert(buildContext != null),

  10.        assert(getState != null),

  11.        _buildContext = buildContext {

  12.    final OnAction onAction = factors.createHandlerOnAction(this);


  13.    /// create Dispatch

  14.    _dispatch = factors.createDispatch(onAction, this, store.dispatch);


  15.    /// Register inter-component broadcast

  16.    _onBroadcast =

  17.        factors.createHandlerOnBroadcast(onAction, this, store.dispatch);

  18.    registerOnDisposed(store.registerReceiver(_onBroadcast));

  19.  }

context中的dispatch是透過factors來進行建立的,factors其實就是當前component,factors建立dispatch的時候傳入了onAction函式,以及context自己和store的dispatch。onAction主要是進行Effect處理。這邊還可以看到,進行context初始化的最後,還將自己的onAction包裝註冊到store的廣播中去,這樣就可以接收到別人發出的action廣播。

Component繼承自Logic

  1. // redux_component/logic.dart


  2.  @override

  3.  Dispatch createDispatch(

  4.      OnAction onAction, Context<T> ctx, Dispatch parentDispatch) {

  5.    Dispatch dispatch = (Action action) {

  6.      throw Exception(

  7.          'Dispatching while appending your effect & onError to dispatch is not allowed.');

  8.    };


  9.    /// attach to store.dispatch

  10.    dispatch = _applyOnAction<T>(onAction, ctx)(

  11.      dispatch: (Action action) => dispatch(action),

  12.      getState: () => ctx.state,

  13.    )(parentDispatch);

  14.    return dispatch;

  15.  }


  16.    static Middleware<T> _applyOnAction<T>(OnAction onAction, Context<T> ctx) {

  17.    return ({Dispatch dispatch, Get<T> getState}) {

  18.      return (Dispatch next) {

  19.        return (Action action) {

  20.          final Object result = onAction?.call(action);

  21.          if (result != null && result != false) {

  22.            return;

  23.          }


  24.          //skip-lifecycle-actions

  25.          if (action.type is Lifecycle) {

  26.            return;

  27.          }



  28.          if (!shouldBeInterruptedBeforeReducer(action)) {

  29.            ctx.pageBroadcast(action);

  30.          }


  31.          next(action);

  32.        };

  33.      };

  34.    };

  35.  }

  36. }

Fish Redux中的Dispatch是怎麼實現的?

上面分發的邏輯大概可以透過上圖來表示

  1. 透過onAction將action交給component對應的effect進行處理

  2. 當effect無法處理此action,且此action非lifecycle-actions,且不需中斷則廣播給當前Page的其餘所有effects

  3. 最後就是繼續將action分發給store的dispatch(parentDispatch傳入的其實就是store.dispatch)

02   store中的dispatch

從store的建立程式碼我們可以看到store的dispatch的具體邏輯

  1. // redux/create_store.dart


  2.  final Dispatch dispatch = (Action action) {

  3.    _throwIfNot(action != null, 'Expected the action to be non-null value.');

  4.    _throwIfNot(

  5.        action.type != null, 'Expected the action.type to be non-null value.');

  6.    _throwIfNot(!isDispatching, 'Reducers may not dispatch actions.');


  7.    try {

  8.      isDispatching = true;

  9.      state = reducer(state, action);

  10.    } finally {

  11.      isDispatching = false;

  12.    }


  13.    final List<_VoidCallback> _notifyListeners = listeners.toList(

  14.      growable: false,

  15.    );

  16.    for (_VoidCallback listener in _notifyListeners) {

  17.      listener();

  18.    }


  19.    notifyController.add(state);

  20.  };

store的dispatch過程比較簡單,主要就是進行reducer的呼叫,處理完成後通知監聽者。

03   middleware

Page繼承自Component,增加了middleware機制,fish-redux的redux部分本身其實就對middleware做了支援,可以透過StoreEnhancer的方式將middlewares進行組裝,合併到Store的dispatch函式中。

middleware機制可以允許我們透過中介軟體的方式對redux的state做AOP處理,比如fish-redux自帶的logMiddleware,可以對state的變化進行log,分別列印出state變化前和變化後的值。

當Page配置了middleware之後,在建立pageStore的過程中會將配置的middleware傳入,傳入之後會對store的dispath進行增強加工,將middleware的處理函式串聯到dispatch中。

  1. // redux_component/component.dart


  2.  Widget buildPage(P param) {

  3.    return wrapper(_PageWidget<T>(

  4.      component: this,

  5.      storeBuilder: () => createPageStore<T>(

  6.            initState(param),

  7.            reducer,

  8.            applyMiddleware<T>(buildMiddleware(middleware)),

  9.          ),

  10.    ));

  11.  }


  12. // redux_component/page_store.dart


  13. PageStore<T> createPageStore<T>(T preloadedState, Reducer<T> reducer,

  14.        [StoreEnhancer<T> enhancer]) =>

  15.    _PageStore<T>(createStore(preloadedState, reducer, enhancer));


  16. // redux/create_store.dart


  17. Store<T> createStore<T>(T preloadedState, Reducer<T> reducer,

  18.        [StoreEnhancer<T> enhancer]) =>

  19.    enhancer != null

  20.        ? enhancer(_createStore)(preloadedState, reducer)

  21.        : _createStore(preloadedState, reducer);

所以這裡可以看到,當傳入enhancer時,createStore的工作被enhancer代理了,會返回一個經過enhancer處理過的store。而PageStore建立的時候傳入的是中介軟體的enhancer。

  1. // redux/apply_middleware.dart


  2. StoreEnhancer<T> applyMiddleware<T>(List<Middleware<T>> middleware) {

  3.  return middleware == null || middleware.isEmpty

  4.      ? null

  5.      : (StoreCreator<T> creator) => (T initState, Reducer<T> reducer) {

  6.            assert(middleware != null && middleware.isNotEmpty);


  7.            final Store<T> store = creator(initState, reducer);

  8.            final Dispatch initialValue = store.dispatch;

  9.            store.dispatch = (Action action) {

  10.              throw Exception(

  11.                  'Dispatching while constructing your middleware is not allowed. '

  12.                  'Other middleware would not be applied to this dispatch.');

  13.            };

  14.            store.dispatch = middleware

  15.                .map((Middleware<T> middleware) => middleware(

  16.                      dispatch: (Action action) => store.dispatch(action),

  17.                      getState: store.getState,

  18.                    ))

  19.                .fold(

  20.                  initialValue,

  21.                  (Dispatch previousValue,

  22.                          Dispatch Function(Dispatch) element) =>

  23.                      element(previousValue),

  24.                );


  25.            return store;

  26.          };

  27. }

這裡的邏輯其實就是將所有的middleware的處理函式都串到store的dispatch,這樣當store進行dispatch的時候所有的中介軟體的處理函式也會被呼叫。下面為各個處理函式的執行順序,

Fish Redux中的Dispatch是怎麼實現的?

首先還是component中的dispatch D1 會被執行,然後傳遞給store的dispatch,而此時store的dispatch已經經過中介軟體的增強,所以會執行中介軟體的處理函式,最終store的原始dispatch函式D2會被執行。

總結

透過上面的內容,現在我們可以知道一個action是如何一步步的派送給effect,reducer去進行處理的,我們也可以透過middleware的方式去跟蹤state的變化,這樣的擴充套件性給框架本身帶來無限可能。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69900359/viewspace-2641585/,如需轉載,請註明出處,否則將追究法律責任。

相關文章