React Best Practice Compilation
26 August 2015Here is a compilation of best practices I have learned and compiled building React.js applications. Feel free to add to this content by visiting my blog source and sending a pull request.
jQuery vs React
jQuery Style
Event Handler <—> Change DOM
React.js Style
Event Handler –> State –> render()
- Event handler changes state. React does a diff in virtual dom and renders.
Properties vs State
- Properties are immutable.
-
State is mutable. State is only reserved for interactiviy. Therefore, anything that is going to change, it goes into State. State should store the most simplest values.
- Props in
getInitialState
is an anti-pattern. - Avoid duplication of “source of truth” which is where the real data is. Whenever possible, compute values on the fly to ensure they don’t get out of sync later on and cause maintenance trouble.
- It is often easier and wiser to move the state higher in component hierarchy
-
You should never alter state directly. Call it through
setState
-
Most importantly, the state of a component should not depend on the props passed in (state as in state of the component - not of the app). *A huge “code smell” is when you start seeing the state depending on the props. For example, this is not good:
constructor(props){ this.state = { fullName: ${props.first} ${props.last}}}
. These guys should go in therender
- Leave calculations and conditionals to the render function.
- Note that everytime
state
is changed,render
is called again.
What Should Go in State?
- State should contain data that a component’s event handlers may change to trigger a UI update. State must be very very small and JSON-serializable. When building a stateful component, think about the minimal possible representation of its state, and only store those properties in
this.state
.
What Should Not Go in State?
- Computed data should not go in state
- React components must not go in state. They must be built in
render
- Duplicated data from props. Try to use props as source of truth where possible.
What Shouldn’t Go in State?
Controlled Inputs
- If you specify a value, you are using a controlled input and you have to provide an
onChange
handler for any controlled input, otherwise it is set in stone. If you’re looking at loading an existing entity and editing it, you want controlled inputs and you want to set the values accordingly - A benefit of controlled inputs is that users can’t edit them, so if you had some properties locked down (maybe for read only, security reasons), when the user attempts to save, even if they edited the HTML directly, React should pull the property’s value from the vDOM representation.
Misc
- use refs instead of target.name
- components can’t be concerned with both presentation and data-fetching
- separating component concerns
PropTypes
- Always use proptypes, especially considering if/when application grows.
- Every
propType
that is notisRequired
should have a corresponding field ingetDefaultProps()
propTypes: {
arrayProp: React.PropTypes.array,
boolType: React.PropTypes.bool,
funcProp: React.PropTypes.func,
numProp: React.PropTypes.number,
objProp: React.PropTypes.object,
stringProp: React.PropTypes.string
}
Avoid State
- Try to avoid state as much as possible. Worst place to call setState is in
componentDidMount
and incomponentDidUpdate
because most render calls will then render twice. - Have the state flow down through your components purely as
props
- have no state actually be managed by components. Set state in stores to hold your application state and have event-driven actions that modify stores - Components must primarily rely on
props
. Instead of callingsetState
, components must communicate with central data store of some sort
Do More in Render()
- avoid logic in
componentWillReceiveProps
orcomponentWillMount
. Instead, move it torender()
Component Organisation
React.createClass({
propTypes: {},
mixins: [],
getInitialState: function(){},
getDefaultProps: function(){},
componentWillMount: function(){},
componentWillReceiveProps: function(){},
componentWillUnmount: function(){},
_parseData: function(){},
_onSelect: function(){},
render: function(){}
});
FLUX
Flux really has three main parts: Actions, Dispatchers and Stores.
Actions are user interactions that occur in your react components, ie: user clicks delete
Dispatchers is a singleton registry.. basically just a centralized list of callbacks. Dispatchers are what talks to stores.
Stores hold your application’s data. When a store’s data is updates, React components changes UI to reflect data. UI never updates the data directly. Data never updates the UI directly. Updates in the UI are rendered due to changes in the store
Actions
Actions encapsulate specific events that occur within the application, ie: save user, delete item.
Dispatcher
Dispatchers are central hub. They contain list of callbacks. Stores register to dispatchers. Dispatcher then broadcasts payload to registered callbacks.
Stores
Stores are where application data and state are located. It contains logic and retrieval. It is not a model - it contains models.
Controller Views
A top level component that flows data down to children. It is the one that interact with stores. It holds data in state. It sends data to children as props.
If you have any questions or comments, please post them below. If you liked this post, you can share it with your followers or follow me on Twitter!