2017-08-03 68 views
1

首先,這裏是我的HOC:如何正確記憶mapDispatchToProps?

export default function connectField({ 
     nameProp = 'name', 
     valueProp = 'value', 
     dispatchProp = 'dispatch' 
    }: ConnectOptions) { 

    return compose(
     getContext(contextTypes), 
     connect((state, ownProps) => { 
      const path = [namespace,...getPath(ownProps),...toPath(ownProps[nameProp])]; 
      const value = getOr('', path, state); 
      return { 
       [valueProp]: value 
      }; 
     }, (dispatch,ownProps) => { // <----------- mapDispatchToProps 
      const path = [...getPath(ownProps),...toPath(ownProps[nameProp])]; 

      return { 
       [dispatchProp]: value => dispatch({type: ActionTypes.Change, payload: {path, value}}) 
      }; 

     }, (stateProps, dispatchProps, {[FIELD_PATH]: _, ...ownProps}) => { 
      return {...stateProps, ...dispatchProps, ...ownProps}; 
     }, { 
      areMergedPropsEqual: (a,b) => { 
       let eq = shallowEqual(a,b); 
       console.log('areMergedPropsEqual',a,b,eq); 
       return eq; 
      }, 
     }), 
     withContext(contextTypes, props => { 
      return {[FIELD_PATH]: [...getPath(props), props[nameProp]]}; 
     }), 
    ); 
} 

在中間有我的mapDispatchToProps。這導致areMergedPropsEqual每次都會返回false,因爲它每次都會創建一個新的動作創建器。

我無法弄清楚如何memoize的該位:

value => dispatch({type: ActionTypes.Change, payload: {path, value}}) 

,這樣我每次得到相同的功能實例。

關於「per-instance memoization」,這是我想要的,但我無法完全理解我應該在這裏做的正面或反面的一些注意事項in the docs


要清楚,我知道如何記憶一個函數。但是,我不希望使用具有無限歷史的大緩存。這是不必要的內存消耗。我只需要像how reselect does it那樣的緩存大小1。問題在於我不能直接在connectField內創建「選擇器」,因爲它仍然會創建一個共享實例 - 即所有「連接的字段」將共享相同的緩存,並且它們將相互覆蓋,從而否定好處。它必須是每個組件實例這是特定於React-Redux的connect方法。它有一個語法,以便您可以在正確的位置創建選擇器,並且每個實例只會運行一次。我只是在解密API時遇到問題 - 他們是否期望一個函數返回一個返回對象的函數?或者是一個帶有名稱作爲鍵和函數作爲值的對象?那個函數返回什麼?即文檔不清楚mapDispatchToProps選項所接受的所有不同變化。

+0

更多的相關信息@KyleRichardson更緊密地閱讀'mapDispatchToProps'。它需要一個「對象*或*函數」。然後,如果函數需要一個或兩個參數,它會再次執行一些不同的操作。然後在「高級場景」下,它說該函數可以*返回一個函數。所以呃...我想我想用一個參數傳遞一個函數並返回一個返回對象的函數。沒關係,我會搗鼓它,直到我弄明白爲止。 – mpen

+1

我很抱歉沒有找到你想要的東西。我現在瞭解這個問題,這是一個非常有趣的問題。仍然在想它......如果你想出一個解決方案,請你更新一下,以便我可以查看想法:) –

回答

0

如果你已經有lodash,你有一個memoize函數,允許將任何函數轉換成memoized函數。這個memoized函數將計算給定參數的返回值,然後總是返回相同的返回值,每個參數都提供相同的參數。

您可以使用它像這樣的例子:

import { memoize } from 'lodash' 

const makeChangeDispatcher = memoize((dispatch, path) => 
    value => dispatch({type: ActionTypes.Change, payload: {path, value}}) 
) 

... 

    (dispatch,ownProps) => { // <----------- mapDispatchToProps 
     const path = [...getPath(ownProps),...toPath(ownProps[nameProp])]; 

     return { 
      [dispatchProp]: makeChangeDispatcher(dispatch, path) 
     }; 

    } 

... 

你可以看到在lodash documentation of memoize

+0

我認爲你錯過了這一點。我遇到的部分是每個實例memoization *。您創建了一個具有無限歷史記錄的單個全局memoized函數。它可能有效,但我的目標是避免由於道具變化而重新渲染。爲此,我只需要1的歷史記錄大小,但它必須是每個實例。對不起,我應該更清楚一點。 – mpen

+0

是的,越來越多的歷史可能是一個潛在的問題,但這是給你一個基於調度功能(總是相同)和取決於你的組件道具的路徑memoized功能。所以你會有實例的功能。正是通過路徑價值泥潭。所以具有相同路徑的兩個組件共享相同的功能(這不是問題)。這裏有什麼問題?我錯過了點^^ –

+0

問題是緩存不需要隨着道具的變化而增長。我所要做的就是避免由於道具變化而重新渲染。如果道具沒有改變,我想回到相同的派遣功能。如果改變了,那麼可能會返回一個新的調度函數。爲此,我只需要1的緩存大小。 – mpen