简体   繁体   English

Redux在reducer之间共享数据和操作的方法

[英]Redux ways to share data and actions between reducers

I am working on a music application that is split between several features: 我正在开发一个音乐应用程序,该应用程序分为以下几个功能:

  • library : displays the available content and allows the user to browse artists and albums, and play one or a few tracks. library :显示可用的内容,并允许用户浏览艺术家和专辑,以及播放一首或几首曲目。 The state looks like this: 状态看起来像这样:

    library { tracksById: {...} albumsById: {...} artistsById: {...} albums: [] artists: [] } 库{tracksById:{...}个专辑ById:{...} artistById:{...}个专辑:[]艺术家:[]}

  • player : the part that plays the music. player :播放音乐的部分。 Just contains an array of all tracks to be played. 仅包含要播放的所有曲目的数组。 Also shows a list of the tracks to be played 还显示要播放的曲目列表

and etc; 等等; The problem is that I would like to share tracksById , albumsById and artistsById between all features, because each album has an artist property, but this property is just an id, so if I want to display the artist name near the track name in the playlist, I need to fetch the artist, and put it in my store. 问题是我想在所有功能之间共享tracksByIdalbumsByIdartistsById ,因为每个专辑都有一个artist属性,但是此属性只是一个id,所以如果我想在播放列表中的曲目名称附近显示艺术家名称, ,我需要提取艺术家并将其放在我的商店中。 I could simply connect them both like this: 我可以像这样简单地将它们连接起来:

@connect(createStructuredSelector({
  player: (state) => state.player,
  library: (state:) => state.library
}), (dispatch) => ({
  actions: bindActionCreators(actions, dispatch)
}))

And then in my playlist view: 然后在我的播放列表视图中:

<div className="artist-name">{this.library.artistsById[track.artist].name}</div>

However this increases coupling between features and I find that it defeats the very purposes of multiple reducers. 但是,这增加了特征之间的耦合,我发现这违背了多个异径管的目的。

Another way would be to create a new feature content , which would only contain the actions and byId properties needed with the most minimal reducer possible and this one would be shared by other features. 另一种方法是创建一个新的功能content ,该content仅包含所需的动作和byId属性,并尽可能使用最小化的reducer,并且此功能将由其他功能共享。 However, this means I have to add a new prop to every component content , and I need to add the actions. 但是,这意味着我必须为每个组件content添加一个新的道具,并且需要添加操作。

I could also create a service, or even maybe a function that would take aa reducer and actions object, and add a logic to make it able to fetch artists, albums and tracks and save them in the store. 我还可以创建一个服务,甚至可以创建一个带有reducer和action对象的函数,并添加逻辑以使其能够获取艺术家,专辑和曲目并将其保存在商店中。 I really like this idea since it decreases coupling, however, it means duplicated data in my state, which means more memory used. 我真的很喜欢这个想法,因为它减少了耦合,但是,这意味着我状态下的数据重复,这意味着要使用更多的内存。

I am not looking for the best way, just wondering if there is any other and what are the pros and cons of each method 我不是在寻找最佳方法,只是想知道是否还有其他方法,每种方法的优缺点是什么

I'm pretty sure I don't fully understand your question, but I'll share some thoughts. 我敢肯定我不会完全理解您的问题,但是我会分享一些想法。 Are you trying to avoid duplicate queries or duplicated state data? 您是否要避免重复查询或重复状态数据?

Objects are fast dictionaries 对象是快速词典

This bit of duplication: 这一点重复:

{this.library.artistsById[track.artist].name}

becomes unnecessary if library.artists is an object instead of an array. 如果library.artists是对象而不是数组,则变得不必要。 Assume 1 and 2 are artist IDs, artists would use the artist ids as keys in the object: 假设1和2是艺术家ID, artists将艺术家ID用作对象中的键:

{
   1: {name: 'Bob Marley', ...etc},
   2: {name: 'Damien Marley', ...etc}
}

Which lets you get artist info from library.artists using only the id: 这样您就可以仅使用ID从library.artists获取艺术家信息:

{this.library.artists[track.artistId].name}

Use selector functions 使用选择器功能

But what you really want is getArtistById(state, artistId){} , a selector function in your reducer code file. 但是,您真正想要的是getArtistById(state, artistId){} ,它是化getArtistById(state, artistId){}代码文件中的选择器函数。 That lets you rearrange the state any time and none of your view components will be affected. 这样,您可以随时重新排列状态,并且不会影响任何视图组件。 If artists is an object with the keys being artist IDs, the implementation could look like this: 如果artists与键在艺术家ID的对象,实施看起来是这样的:

getArtistById(state, artistId){
    return state.library.artists[artistId]
}

I agree with your observation that accessing library.artistsById[track.artist].name adds unnecessary coupling. 我同意您的观察,即访问library.artistsById[track.artist].name会增加不必要的耦合。 I would never put code that knows about high-level state shape into a view component. 我永远不会将了解高级状态形状的代码放入视图组件。 For your example playlist view with a list of artists, each item in the playlist view would be its own tiny component. 对于带有艺术家列表的示例播放列表视图,播放列表视图中的每个项目都是其自己的微型组件。 The container would look like this: 容器看起来像这样:

import { getArtistById } from '../reducers/root-reducer'
const mapStateToProps = (state) => {
   const artist = getArtistById(state.library)
   return { artistName: artist ? artist.name : '' }
}
...

The tiny view component would look something like: 微小的视图组件看起来像:

render() { 
    const {artistName} = this.props
    return <div className="artist-name">{artistName}</div> 
}

Premature optimization? 过早的优化?

You might be prematurely optimizing. 您可能过早地进行了优化。 Even if you keep artists and albums as arrays, your selector function can search through the array for the ID. 即使将artistsalbums保留为数组,选择器函数也可以在数组中搜索ID。 A function to get each of those by id from a state that only contains { artists: [], albums: [], tracks:[] } should be very fast until the library got pretty huge. 从仅包含{ artists: [], albums: [], tracks:[] }的状态通过id获取每个标识符的函数应该非常快,直到库变得非常庞大为止。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM