- Store the minimum amount of data necessary to describe the entire state of the app
- Use selectors to derive information from the store
- Directory that matches your state tree structure.
-
The top level state should be singular and camelCase (
contact
notcontacts
,contactGroup
notcontactGroups
) -
Each branch of the tree is its own directory.
-
Don't nest state directories because it doesn't add a lot of value. Keep it flat.
-
Just have one reducer, one actions, and one selector file for each part of the state
components Button.js Icon.js MessageInput.js state actionTypes.js store.js contact index.js (reducer) contactActions.js (action creators) contactSelectors.js contactGroup index.js contactGroupActions.js contactGroupSelectors.js
-
-
Use actions types to describe actions that the user starts and any resulting success or failure from the server.
-
Action types should not describe every part of the state, but general actions that the user can take.
For example, you don't need all of these action types when the user clicks save to update their profile:
CONTACT__NAME_UPDATED CONTACT__ADDRESS_UPDATED CONTACT__PHONE_NUMBER_UPDATED
because you could just have one action type that describes all possible changes to their profile.
CONTACT__UPDATED
-
Action types file should export an object where the keys and values are the action types
export default createConstantObject( 'CONTACT__LOAD_REQUESTED', 'CONTACT__LOAD_SUCEEDED', 'CONTACT__LOAD_FAILED', );
-
Import actions types as TYPES
import TYPES from '../actionTypes'
-
Action type names
- All uppercase
- Last part should be a verb in the past tense
TOP_STATE__THING_HAPPENED | | | |- Describes the action in past tense (`CONTACTS_ADDED`, `CONTACTS_REMOVED`, `STARRED`, `MARK_UNREAD_FAILED`) | |- The name of the top level state (`CONTACT`, `CONTACT_GROUP`)
-
Async actions
- Use 3 action TYPES that end in
REQUESTED
,SUCCEEDED
,FAILED
- Use 3 action TYPES that end in
-
Async actions should use redux thunk.
-
The consumer should not be able to tell the difference between calling an synchronous or asynchronous action creator.
-
Async actions should optimistically update the state and roll it back if the request fails.
-
Async actions can also set an
isLoading
flag on request and set it back tofalse
on success or fail.
- Use services for api calls and side effects and localstorage (use promises)
fetchMessages().then(() => {}, () => {}) // not .catch()
-
Use selector functions instead of accessing the store directly
-
Export the presentational component for testing and Storybook
-
Export the container component as default
-
You can memoize your selectors
-
Boolean props
- Should start with
is
orhas
- Should start with
-
Callback props
- Callback prop names should start with
on
(onClick
,onToggle
). - Functions passed to callback props should start with
handle
(<Button onClick={this.handleBackClick}>Back</Button>
).
- Callback prop names should start with
-
Render props
- Render props are usually better than higher order components.
- Make sure you have
propTypes
on children (children: PropTypes.func.isRequired
).