Redux Standard Props
Organize Redux specific props in components under a dedicated namespace.
-
Published
-
Categories
Components connected to Redux have three types of props:
- values that derive from reducers
- functions that dispatch actions
- values/functions that make up a components public API
The props passed to the Films
component below best illustrates this:
Programming language (abbreviated): jsx
const Films = ({
genre,
films,
hasRecommendToFriend,
onWatchLater,
onPlay
}) => {
const [film, setFilm] = useState("");
const handleFilm = ({ target: { value } }) => setFilm(value);
const handleSubmit = event => {
event.preventDefault();
onWatchLater(film);
};
return (
<form onSubmit={handleSubmit}/>
<h3/>{genre}</h3/>
<select value={film} onChange={handleFilm}/>
{films.map(({ id, title }) => (
<option key={id} value={id}/>
{title}
</option/>
))}
</select/>
{hasRecommendToFriend && film && <RecommendToFriend film={film} />}
<button type="button" onClick={onPlay}/>
Play
</button/>
<button/>Watch Later</button/>
</form/>
);
};
const mapStateToProps = ({ genre, films }) => ({
genre,
films
});
const mapDispatchToProps = dispatch => ({
onWatchLater: film => dispatch(watchLater(film))
});
One thing the different types of props lack is an identity.
It is difficult to understand where each prop comes from without digging into the code. Is genre passed from a parent component or does it derive from a reducer? Is onWatchLater
a prop callback or an action?
A quick scan of mapStateToProps
and mapDispatchToProps
reveals prop types 1 and 2, which subsequently surfaces prop type 3 (by process of elimination) however there's a fair amount of flip-flopping involved.
This can be remedied by grouping Redux specific props under a dedicated namespace:
Programming language (abbreviated): js
store: {
// holds values derived from reducers
},
actions: {
// holds functions that dispatch actions
}
Below is a revised version of the Films
component using this approach:
Programming language (abbreviated): jsx
const Films = ({
store,
actions,
hasRecommendToFriend,
onPlay
}) => {
const [film, setFilm] = useState("");
const handleFilm = ({ target: { value } }) => setFilm(value);
const handleSubmit = event => {
event.preventDefault();
actions.onWatchLater(film);
};
return (
<form onSubmit={handleSubmit}/>
<h3/>{store.genre}</h3/>
<select value={film} onChange={handleFilm}/>
{store.films.map(({ id, title }) => (
<option key={id} value={id}/>
{title}
</option/>
))}
</select/>
{hasRecommendToFriend && film && <RecommendToFriend film={film} />}
<button type="button" onClick={onPlay}/>
Play
</button/>
<button/>Watch Later</button/>
</form/>
);
};
const mapStateToProps = ({ genre, films }) => ({
store: {
genre,
films
}
});
const mapDispatchToProps = dispatch => ({
actions: {
onWatchLater: film => dispatch(watchLater(film))
}
});
Now it is explicitly clear that store.films
comes from Redux state and actions.onWatchLater
dispatches an action. All other props (those not within the store and actions namespace) make up the components public API, which are usually the props consumers care most about.
About the author
I'm Callum, a Front-end Engineer at Nutmeg. Previously I wrote code for KAYAK, American Express, and Dell. Out of hours I publish blog posts (like this one) and tweet cherry-picks.
Feel free to follow or message me at @_callumhart on Twitter.