2016-11-04 21 views
4

我在這裏所描述的「registerEpic的效用:Is it an efficient practice to add new epics lazily inside react-router onEnter hooks?終極版,可觀察到的多個動作

我們的代碼需要isomoprhic,但在服務器端,一個動作被觸發的第一時間,一切都好。但是,第二次觸發行動時,史詩似乎會得到該行動的兩個副本。這裏是我的代碼:

export const fetchEpic = (action$, store) => 
    action$.ofType("FETCH") 
    .do((action) => console.log('doing fetch', action.payload)) 
    .mergeMap(({meta:{type}, payload:[url, options = {}]}) => { 

      let defaultHeaders = { 
       'Accept': 'application/json', 
       'Content-Type': 'application/json' 
      }; 

      options.headers = {...defaultHeaders, ...options.headers}; 

      let request = { 
       url, 
       method: 'GET', 
       responseType: 'json', 
       ...options 
      }; 

      //AjaxObservables are cancellable... that's why we use them instead of fetch. Promises can't be cancelled. 

      return AjaxObservable.create(request) 
       .takeUntil(action$.ofType(`${type}_CANCEL`)) 

       .map(({response: payload}) => ({type, payload})) 
       .catch(({xhr:{response: payload}}) => (Observable.of({type, payload, error: true}))); 

     } 
    ); 

registerEpic(fetchEpic); 

所以第一時間我打觸發此動作(服務器端)一切工作正常,我在控制檯中得到「做取」一次一個頁面。

但是,刷新頁面,這些控制檯消息和結果行爲的收益率2不會被觸發。

我加了一個「清除」功能,我的史詩註冊表,但也許我總小白醬,只是沒有完全神交它。這是我的中間件:

let epicRegistry = []; 
let mw = null; 
let epic$ = null; 
export const registerEpic = (epic) => { 
    // don't add an epic that is already registered/running 
    if (epicRegistry.indexOf(epic) === -1) { 
     epicRegistry.push(epic); 

     if (epic$ !== null) { //this prevents the observable from being used before the store is created. 
      epic$.next(epic); 
     } 
    } 
}; 

export const unregisterEpic =(epic) => { 
    const index = epicRegistry.indexOf(epic); 
    if(index >= 0) { 
     epicRegistry.splice(index, 1); 
    } 
} 

export const clear =() => { 
    epic$.complete(); 
    epic$ = new BehaviorSubject(combineEpics(...epicRegistry)); 

} 

export default() => { 

    if (mw === null) { 
     epic$ = new BehaviorSubject(combineEpics(...epicRegistry)); 
     const rootEpic = (action$, store) => 
      epic$.mergeMap(epic => epic(action$, store)); 

     mw = createEpicMiddleware(rootEpic); 
    } 

    return mw; 
}; 

回答

1

我沒有在任何地方看到epicMiddleware.replaceEpic?如果您沒有更換當前正在運行的中間件內部史詩,確實很難確保它沒有采取行動 - 這聽起來與此處發生的情況完全相同。

做服務器端渲染與Redux的可見的(或任何異步中間件,包括終極版,傳奇)是艱難的,因爲你不得不創造一些約定。例如當你的應用程序啓動時,如果一部史詩開始產生一些副作用,那麼你什麼時候說「這就夠了」並過早地將其切斷?你是否讓他們做任何異步?你如何妥善保證史詩真正結束其所有副作用?這是可能中間件停止訂閱史詩,但它沒有正確地鏈接其用戶,所以繼續一些副作用無論如何。

這就是爲什麼我們還沒有證明如何在終極版,可觀察到的SSR做,因爲當我們創建代碼,「它」,我們不是在選擇充滿信心。

你怎麼決定處理它,很可能你最終需要調用epicMiddleware.replaceEpic(nextRootEpic)讓中間件停止訂閱上一個根史詩。

如果你不想讓史詩般的任何異步在一個頁面渲染serverside期間,你會呈現頁面同步,然後立即用相同的根史詩再次替換根史詩。也就是說,你幾乎肯定會再次傳遞完全相同的根史詩,這將有效地「重新啓動」你的史詩樹 - 但如果你寫了一部行爲異常的史詩,再一次沒有保證。

我很高興,以幫助進一步闡述,如果你想發佈您的實際代碼SSR,例如如果你使用React和react-router,那將會是你所有的東西match({ routes, location: req.url } ...etc)


這是值得注意的是,很多人都認爲SSR should not do any side effects,並讓他們到客戶端。因此,沒有AJAX調用,在React可觀察的情況下,這意味着不要派發引起副作用的動作,直到componentDidMount(這不是運行serverside)。這主要是因爲在很多方面,這會打敗SSR可能帶來的性能優勢,因爲您不會盡快發送最初的HTML,讓JS走到一邊。然而,通常沒有提到的是,如果你不拉動任何動態數據,你的應用程序顯然沒有那麼多的搜索引擎優化。這是一個折衷,你必須決定,或者可能混合使用兩者。

+0

@Ben是否回答了你的問題? – jayphelps