React Packet
React Packet is an alternative interface to react-redux
that helps you hide the internal complexity of action creators and selectors from your components and encapsulates that logic in topic-based files.
Packet helps you to think in terms of Users
, Projects
, Groups
, OtherBusinessObjects
instead of separating out selectors
, action creators
, and reducers
.
Instead of this in your components:
;;;const mapStateToProps = { return users: ;};const mapDispatchToProps = { return ;};mapStateToProps mapDispatchToPropsUserList;
Write this:
;;usersUserList;
Installation
If you already have react-redux
and redux
in your project, run
npm install --save react-packet
Otherwise,
npm install --save redux react-redux react-packet
Quick Start
Create packets of state that can be used across your app:
// import packAll where you deal with Redux state, to create a nice consumable packet of state and actions; const users = ;
Then consume them in React components:
// import consume where you create a Redux-connected React.Componentimport consume from 'react-packet'; // a stateless component that might want some user infoconst UserList = users loadUsers <ul>users</ul> <button =>Load</button>; // grab the packets you want for your current context and pass them to consume() to create a higher-order component.const withUsersForGroup = ;const GroupUserList = ;ReactDOM
API Reference
pack(packetDescription : PacketDescription) : PacketMaker
Given a packetDescription
return a generated Packet
function.
PacketDescription { selector: Selector|()=>Selector, actions: Actions:{}}
Selector (state[, props]) => stateProps
Actions (dispatch[, props]) => dispatchProps
PacketMaker (...contextSelectors) => Packet
Packet { mapStateToProps, mapDispatchToProps, minimumSelectorsExpected?: number }
packAll(packetDescriptionMap : PacketDescriptionMap) : { [key: string]: PacketMaker }
Given an object where each property is a packetDescription
, return a new object with the same property names whose values are the generated Packet
s.
PacketDescriptionMap { [key: string]: PacketDescription }
consume(packets[, mapPacketsToProps[, mergeProps[, connectOptions]]]) : Component => ConnectedComponent
packets Array|Packet
mapPacketsToProps(...packetProps) (...Array<{}>) => {}
Takes in the stash and dispatch props from each packet as a separate argument and combines all properties into one object. For example,
it might be called like mapPacketsToProps({ users, loadUsers }, { projects, loadProjects })
. By default each arguments will be combined into
a single object with Object.assign
.
mergeProps(allPacketProps, ownProps) ()
Combine the packed properties with any properties passed in from the parent. Be default this will use Object.assign({}, ownProps, allPacketProps)
.
Longer Example
Redux states are generally normalized so that details about an entity can be shared and updated as new data comes in. That means when thinking about users, groups, projects, andt he relationships between them, you might have state like this:
But when working with this state in your code, you typically don't want to deal with all that normalized state and understanding how it fits together. Ideally you'd handle that all in one place and expose it in a more intuitive shape. This is where React Packet comes in. It encourages you to create that "one place".
pack()
pack()
is how you create that place. pack
lets you define selectors and action creators that take in any context from the caller, and output the appropriate props and actionCreators. Below you'll see a users.forGroup(props => props.groupId)
packet and a users.forProject(props => props.projectId)
packet. Each has a selector
and an actions
property that deliver tailored data about users.
// state/users.js ; forGroup: { // ...combine all the various bits of state... const usersForGroup = stateusersByGroupgroupId; return ...usersForGroup users: usersForGroupusers ; // returns { // users, // isLoading, // hasAllLoaded, // errors // } } forProject: { // ...combine all the various bits of state... const usersForProject = stateusersByProjectprojectId; return users: users;
consume()
consume()
is how you consume a packet. You call it with your packets, passing in any required context parameters. It's a higher-order component that will call connect()
under the hood and provide your component with the packet properties.
const UserList = users = isLoading hasAllLoaded errors loadUsers <div> errors && <ul>errors</ul> isLoading && <Spinner /> <ul>users</ul> hasAllLoaded || <button =>Load users</button> </div>;
;;; const ProjectUserList = UserList;const GroupUserList = UserList;// <ProjectUserList projectId="projectA" />// <GroupUserList groupId="groupA" />
You can call consume()
with multiple packets, but if any properties overlap, you'll have to combine them yourself with a mapPacketsToProps
:
const CompareUserLists = <div> <UserList ...listA /> <UserList ...listB /> </div>; const CompareUsersInGroups = CompareUserLists; // <CompareUsersInGroups groupA="groupA" groupB="groupB" />