Embracing Functions in React

Stateless Functional Components and Why they Matter

Introduction

This post is not about any best practices or "the only way" to write your application with React.

This writeup is all about stateless functional components in React and why they might be useful or should be considered in the first place.

Definition

Before we get down to the nitty-gritty, let’s begin with a definition of what functional components mean in a React context. In essence it’s a regular function that expects props and returns an element.

function Item(props) {
return (
<div className='item'>
{props.title}
<span
className='deleteItem'
onClick=
{props.remove(props.id)}
> x </span>
</div>
)
}

Or with ES6 arrow functions and destructuring we could write this in a clearer fashion:

const Item = ({ id, title, remove }) => <div className='item'>
{title}
<span
className='deleteItem'
onClick=
{remove(id)}
> x </span>
</div>

At first glance this might seem as nothing special or that choosing between using createClass, ES6 Class and stateless functional components is just a matter of style. From my point of view there is more to it.

Let’s see the difference between using function component and the class based approach and see what implications we can derive based on the given facts.

No lifecycle methods

Functional components, sometimes also referred to as stateless components have no lifecycle methods, meaning they are rendered every time a state change occurs higher up the component tree due to the missing shouldComponentUpdate method. This also implies you can’t define certain actions based on the fact if a component did mount or will unmount for example.

No this. No refs.

More interesting is the fact that with functional components you neither have a this keyword nor access to ref. Which might be surprising to some coming from a strictly class or object oriented style. This might be the strongest argument for using functions as we will see later on.

Another interesting fact is that you can still have access to context if you declare the context as a property of the function.

Why even consider Functions as the de-facto way?

So where is the advantage you might be asking yourself. Especially if you have switched to React lately or prefer a class based approach. The concept of container components and presentational components becomes very clear when we favor this concept. Also read the Dan Abramov post on the topic for a more in depth explanation.

By separating any logic and data handling from the UI representation we avoid any state handling inside representational components by default. Stateless functional components enforce this style, as there is no way to handle local state whatsoever. It forces you to move any state handling higher up the component tree, letting the lower level do what they intend to do, take care of the UI representation.

Instead of quickly adding some local state and writing a todo comment claiming to refactor it later on, some one has to figure out where the state belongs to in the first place.

No logic means same data in same representation out.

Avoiding Common Pitfalls

You still need to avoid certain patterns when writing stateless functional components. Defining a function inside the function component should be avoided, as a new function will be created every time the functional component is called.

const Form = ({...}) => {
const handleSomething = e => path(['event', 'target'], e)
return (
// ...
)
}

This can be easily solved by either passing the function as prop or defining it outside the component.

const handleSomething = e => path(['event', 'target'], e)
const Form = ({...}) => // ...

Sometimes the word pure is thrown into the mix when talking about stateless functional components. You should avoid using context or defaultProps in this regards and opt for a class based approach if you need to define either or both of the aforementioned.

const ListComponent = ({...})
ListComponent.contextTypes = {
style: React.PropTypes.object.isRequired
}
ListComponent.defaultProps = {
items: []
}

In the case of defaultProps a workaround would be to favor default arguments.

const ListComponent = ({ items = [] }) => (...)

Check a comment by Bernhard Gschwantner regarding purity, that summarizes the facts perfectly.

Another common pitfall is thinking that by simply using pure stateless functions we gain performance wins for free and always. This is not true. In fact the opposite is true once we’re dealing with a large number of stateless functional components.

The performance gains are derived by the lack of lifecycle methods, which means React doesn’t call any additional functions and always renders the component.

Lacking lifecycle methods leads to not being able to define a shouldComponentUpdate method on the other hand. Now, we can’t tell React to re-render or not, it will always re-render. This situation can be mitigated as we will see.

Higher Order Components

If you want to learn more about higher order components and how we can benefit from using them, take a look at Why The Hipsters Recompose Everything.

High order components are functions that expect a component and return a new component.

HOC :: Component -> Component

This approach enables us to solve a number of problems that might arise when working with stateless functional components. See above post for a more thorough run through, but for clarity we can solve state handling and render optimization by simply wrapping our function components into higher order components that takes care of local state handling and/or implement a shouldComponentUpdate function.

Recompose takes care of the aforementioned situations.

The following example is lifted straight from the projects README file.

//component that is expensive to render
const ExpensiveComponent = ({ propA, propB }) => {...}

// Same effect as React's PureRenderMixin
const OptimizedComponent = pure(ExpensiveComponent)

// more optimized: only updates if specific prop keys have changed
const HyperOptimizedComponent =
onlyUpdateForKeys(['propA', 'propB'])(ExpensiveComponent)

As seen above we can focus on the UI representation and when needed wrap the function into a pure method and export the wrapped function. We don’t need to refactor the original function into a class component.

The next example is from the Why The Hipsters Recompose Everything.

const withState = (stateName, stateUpdateFn, initialState) => {
return Comp => {
const factory = createFactory(Comp)
return createClass({
getInitialState() { return { value: initialState } },
stateUpdateFn(fn) {
this.setState(({ value } ) => ({ value: fn(value) }))
},
render() {
return factory({
...this.props,
[stateName] : this.state.value,
[stateUpdateFn] : this.stateUpdateFn,
})
}
})
}
}

withState enables us to manage local component state when needed, by passing our stateless function component into the enhance function.

const enhance = withState('counter', 'setCounter', 0)
const Counter = enhance(({ counter, setCounter }) =>
<div>
Count: {counter}
<button onClick={() => setCounter(n => n+1)}>Increment</button>
<button onClick={() => setCounter(n => n-1)}>Decrement</button>
</div>
)

Again, recompose already implements withState, so no need to take care of implementing it yourself.

Outro

The biggest benefits of stateless functional components are a clear separation between container and representational components, avoiding large and messy components. No this keyword means no shortcuts and spreading state randomly all over the application.

These aspects become extra useful when working inside a team with differing levels of experience and expertise, helping indirectly to enforce internal standards.

Any thoughts? Let me know.

Any questions or Feedback? Connect via Twitter