Housekeeping
Compute initial state from initial props
If the initialState
parameter of withState()
is a function, it is used to compute the initial state given the initial props. For example, to create a counter that starts at a certain value:
const Counter = compose(
withState('count', 'setCounter', props => props.initialCount || 0),
mapProps({ setCounter, ...rest } => ({
increment: () => setCounter(n => n + 1),
decrement: () => setCounter(n => n - 1),
...rest
}))
)(({ count, increment, decrement }) => (
<p>
Count: {count}
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</p>
));
ReactDOM.render(<Counter initialCounter={7} />);
I'm trying to generally avoid function overloading but in some cases (like this one) it makes sense.
Internal optimization: Avoid unnecessary intermediate React elements
Higher-order components are useful and provide a nice programming model, but they aren't free: there is a performance overhead when introducing a new component to the tree. I suspect this cost is negligible compared to the gains achieved by blocking subtrees from re-rendering using shouldComponentUpdate
— which Recompose makes easy with its shouldUpdate()
and onlyUpdateForKeys()
helpers. I'll try to put together some benchmarks so we know what we're dealing with.
However, many of Recompose's higher-order component helpers are implemented using stateless function components rather than class components. Eventually, React will include optimizations for stateless components. Until then, we can do our own optimizations by taking advantage of referential transparency. In other words, creating an element from a stateless function is effectively* the same as calling the function and returning its output.
To accomplish this, Recompose uses a special version of createElement()
that returns the output of stateless functions instead of creating a new element. For class components, it uses the built-in React.createElement()
.
* Stateless function components are not referentially transparent if they access context; we detect that by checking for the existence of contextTypes
.