簡體   English   中英

React Redux - 我可以讓mapStateToProps只占用部分狀態嗎?

[英]React Redux — can I make mapStateToProps only take in part of the state?

我想制作可以插入任何react-redux應用程序的可重用模塊。 理想情況下,我的模塊在頂層有一個容器組件,操作和reducer(然后是容器下面的任何表示組件)。 我希望模塊只能處理自己應用程序狀態的一部分,理想情況下不必了解應用程序狀態的其余部分(因此它是真正的模塊化)。

Reducers只能在部分狀態下工作(使用combineReducers),所以我很高興。 但是,對於容器組件,mapStateToProps似乎總是處於應用程序的完整狀態。

如果mapStateToProps只接受我在模塊中處理的相同“狀態切片”(就像reducer一樣),我會喜歡它。 這樣我的模塊就會真正模塊化。 這可能嗎? 我想我可以把狀態的那一部分傳遞給這個組件的道具(所以我可以使用mapStateToProps的第二個參數,ownProps),但我不確定這是否具有相同的效果。

這實際上是一個復雜的話題。 因為Redux是一個單一的全局存儲,所以完全封裝,完全可重用的即插即用邏輯集的想法確實變得相當困難。 特別是,雖然reducer邏輯可以相當通用並且不知道它在哪里,但是選擇器函數需要知道樹中的哪個位置才能找到該數據。

你的問題的具體答案是“不, mapState總是給出完整的狀態樹”。

我確實有許多相關資源的鏈接,這可能對您的情況有所幫助:

如你所知,Redux只有一個商店,所以它知道要做的就是將整個商店傳遞給你的mapStateToProps函數。 但是,使用對象解構,您可以指定所需的存儲中的哪些屬性,而忽略其余屬性。 像'function mapStateToProps({prop1,prop2})'這樣的東西只能捕獲商店中的那兩個屬性而忽略其余的屬性。 您的功能仍在接收整個商店,但您只是指出這些道具只對您感興趣。

在我的例子中,'prop1'和'prop2'將是你在'combineReducers'調用期間為reducers指定的名稱。

理想情況下,它的工作方式是獲取狀態,然后使用解構器從中提取值。 redux適用於單一狀態的概念

例如:-

function mapStateToProps(state){
    const { auth } = state  //just taking a auth as example.
    return{
      auth
    }
}

我遇到了同樣的問題,因為正如你所說,redux / react-redux的當前實現允許在狀態上拆分reducers,但mapDispatchToProps 總是傳遞整個狀態樹。

https://stackoverflow.com/a/39757853/444794不是我想要的,因為這意味着我們必須在使用我們模塊的每個react-redux應用程序中復制所有選擇器邏輯。

我目前的解決方法是將狀態切片作為支柱傳遞下去。 這遵循一種組成模式,但同時消除了直接進入國家的清潔,我對此感到失望。

例:

通常,您想要這樣做:

const mapStateToProps = (state) => {
  return {
    items: mySelector(state)
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    doStuff: (item) => {
      dispatch(doStuff(item))
    }
  }
}

class ModularComponent extends React.Component {
  render() {
    return (
      <div>
        { this.props.items.map((item) => {
          <h1 onclick={ () => this.props.doStuff(item) }>{item.title}</h1>
        })}
      </div>
    )
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(ModularComponent)

但由於此模塊包含在一個應用程序中,其中狀態現在是幾個事物(即鍵值)而不是項目列表,這將不起作用。 我的解決方法看起來像:

const mapStateToProps = (_, ownProps) => {
  return {
    items: mySelector(ownProps.items)
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    doStuff: (item) => {
      dispatch(doStuff(item))
    }
  }
}

class ModularComponent extends React.Component {
  render() {
    return (
      <div>
        { this.props.items.map((item) => {
          <h1 onclick={ () => this.props.doStuff(item) }>{item.title}</h1>
        })}
      </div>
    )
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(ModularComponent)

使用該模塊的應用程序如下所示:

const mapStateToProps = (state) => {
  return {
    items: state.items
    stuffForAnotherModule: state.otherStuff
  }
}

class Application extends React.Component {
  render() {
    return (
      <div>
        <ModularComponent items={ this.props.items } />
        <OtherComponent stuff={ this.props.stuffForAnotherModule } />
      </div>
    )
  }
}

export default connect(mapStateToProps)(Application)

雖然mapStateToProps (你傳遞給connect的第一個函數)如你所說的那樣傳遞整個商店,但它的工作是將狀態的特定部分映射到組件。 因此,只有從mapStateToProps返回的內容才會被映射為組件的prop。

所以,讓我們說你的州看起來像這樣:

{
    account: {
        username: "Jane Doe",
        email: "janedoe@somemail.com",
        password: "12345",
        ....
    },
    someOtherStuff: {
        foo: 'bar',
        foo2: 'bar2'
    },
    yetMoreStuff: {
        usuless: true,
        notNeeded: true
    }
}

並且你的組件需要來自someOtherStuff accountfoo所有內容,那么你的mapStateToProps將如下所示:

const mapStateToProps = ({ account, someOtherStuff }) => ({
    account,
    foo: { someOtherStuff }
});
export default connect(mapStateToProps)(ComponentName)

然后你的組件將具有從你的redux狀態映射的prop accountfoo

你可以選擇為你的模塊編寫幾個包裝工具來完成以下工作:1)只有當模塊的狀態切片發生變化時才運行mapStateToProps,2)只將模塊的切片傳遞給mapStateToProps。

這都假設您的模塊狀態切片是app狀態對象上的根屬性(例如state.module1state.module2 )。

  1. 自定義areStatesEqual包裝函數,確保mapStateToProps僅在模塊的子狀態更改時才會運行:
function areSubstatesEqual(substateName) {
  return function areSubstatesEqual(next, prev) {
    return next[substateName] === prev[substateName];
  };
}

然后將其傳遞給connect

connect(mapStateToProps, mapConnectToProps, null, {
  areStatesEqual: areSubstatesEqual('myModuleSubstateName')
})(MyModuleComponent);
  1. 自定義mapStateToProps包裝器,只傳遞模塊子狀態:
function mapSubstateToProps(substateName, mapStateToProps) {
  var numArgs = mapStateToProps.length;

  if (numArgs !== 1) {
    return function(state, ownProps) {
      return mapStateToProps(state[substateName], ownProps);
    };
  }

  return function(state) {
    return mapStateToProps(state[substateName]);
  };
}

而且你會這樣使用它:

function myComponentMapStateToProps(state) {
  // Transform state
  return props;
}
var mapSubstate = mapSubstateToProps('myModuleSubstateName', myComponentMapStateToProps);

connect(mapSubstate, mapDispatchToState, null, {
  areStatesEqual: areSubstatesEqual('myModuleSubstateName')
})(MyModuleComponent);

雖然未經測試,但最后一個示例只應在'myModuleSubstateName'狀態更改時運行myComponentMapStateToProps ,並且它只接收模塊子狀態。

另外一個增強功能可能是編寫自己的基於模塊的connect函數,該函數需要一個額外的moduleName參數:

function moduleConnect(moduleName, mapStateToProps, mapDispatchToProps, mergeProps, options) {
  var _mapState = mapSubstateToProps(moduleName, mapStateToProps);
  var _options = Object.assign({}, options, {
    areStatesEqual: areSubstatesEqual('myModuleSubstateName')
  });
  return connect(_mapState, mapDispatchToProps, mergeProps, _options);
}

然后每個模塊組件只需要做:

moduleConnect('myModuleName', myMapStateToProps)(MyModuleComponent);

你的問題的答案是肯定的。 給出的答案都涵蓋同一事物的不同方面。 首先,Redux創建了一個包含多個reducer的商店。 所以你會想要將它們組合起來:

export default combineReducers({
  people: peopleReducer,
  departments: departmentsReducer,
  auth: authenticationReducer
});

然后,假設您有一個DepartmentsList組件,您可能只需要將departments從商店映射到組件(也可能是映射到props的一些操作):

function mapStateToProps(state) {
  return { departments: state.departments.departmentsList };
}

export default connect(mapStateToProps, { fetchDepartments: fetchDepartments })(DepartmentsListComponent);

然后在組件內部基本上是:

this.props.departments
this.props.fetchDepartments()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM