简体   繁体   中英

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. The state looks like this:

    library { tracksById: {...} albumsById: {...} artistsById: {...} albums: [] artists: [] }

  • player : the part that plays the music. 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. 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. However, this means I have to add a new prop to every component content , and I need to add the actions.

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. 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. Assume 1 and 2 are artist IDs, artists would use the artist ids as keys in the object:

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

Which lets you get artist info from library.artists using only the id:

{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. 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:

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

I agree with your observation that accessing library.artistsById[track.artist].name adds unnecessary coupling. 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. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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