Reputation: 7656
Here is the problem I am having. I have to pass id={this.props.id}
to each component so that I can get the correct player from the list of players in mapStateToProps
by looking at ownProps.
My question is:
How can I get the correct player out of the list without having to pass the id to each of my components like in the code below?
If mapStateToProps
accepted a context
param then I could just get the id from the context in each of the components and pass it down once.
Is their a better way of doing this?
AudioPlayers that the developer defines:
class AudioPlayer extends React.Component {
render() {
return (
<Player className="jp-default" id={this.props.id}>
<Gui id={this.props.id}>
<Media id={this.props.id}>
<Audio id={this.props.id}>
</Audio>
</Media>
<div className="poster-container">
<Poster id={this.props.id}/>
<Title id={this.props.id}/>
</div>
</Gui>
</Player>
);
}
}
AudioPlayer.options = {
id: "audio-player",
paused: true,
//...etc
};
class AudioPlayerTwo extends React.Component {
render() {
return (
<Player className="jp-default-two" id={this.props.id}>
<Gui id={this.props.id}>
<Media id={this.props.id}>
<Audio id={this.props.id}>
</Audio>
</Media>
<div className="poster-container">
<Poster id={this.props.id}/>
<Title id={this.props.id}/>
</div>
</Gui>
</Player>
);
}
}
AudioPlayerTwo.options = {
id: "audio-player-two,
paused: true,
//...etc
};
createPlayers([AudioPlayer, AudioPlayerTwo]);
Code that is going to be an npm package:
export createPlayers (WrappedPlayers) => {
const store = createStore(combineReducers({players: playerReducer}));
ReactDOM.render(
<Provider store={store}>
<div>
{WrappedPlayers.map(WrappedPlayer => {
const ConnectedPlayer = connect(mapStateToProps, mapDispatchToProps)(WrappedPlayer);
<ConnectedPlayer key={WrappedPlayer.options.id} id={WrappedPlayer.options.id} />
})}
</div>
</Provider>,
document.getElementById("app"));
}
actions:
export const play = (id, time) => ({
type: actionTypes.player.PLAY,
time,
id
});
...etc
reducer:
const play = (state, action) => {
if(state.srcSet) {
return updateObject(state, {
paused: false,
newTime: !isNaN(action.time) ? action.time : state.currentTime
});
}
}
export default (state={}, action) => {
//Only update the correct player
const currentPlayer = state[action.id];
let newState = {...state};
switch (action.type) {;
case actionTypes.player.PLAY:
newState = play(currentPlayer, action);
break;
default:
return state;
}
return updateObject(state, {
[action.id]: newState
});
}
All of my components look similar to this:
Notice how I access the correct player from the list with the props id
.
const mapStateToProps = (state, props) => ({
paused: state.players[props.id].paused,
id: props.id
});
const Play = (props) => {
const onPlayClick = () => props.paused ? props.dispatch(play(props.id)) : props.dispatch(pause(props.id))
return <a onClick={onPlayClick}>{props.children}</a>
}
export default connect(mapStateToProps)(Play);
Upvotes: 0
Views: 634
Reputation: 8295
You can solve this in userland by making your own wrapper on connect
+ getContext
(from recompose) and use that in your app instead of vanilla connect. It'd look something like
import { connect } from 'react-redux'
import { compose, getContext } from 'recompose'
export default function connectWithMyId(...args) {
return compose(
getContext({ myId: PropTypes.string }),
connect(...args)
)
}
And then myId
would be available as a prop in your mapStateToProps
.
Upvotes: 4