Getting started with React Recoil
18th May 2020
Recoil is a state management library for React by facebook.
Overview
Recoil lets you create a data-flow graph that flows from atoms (shared state) through selectors (pure functions) and down into your React components. Atoms are units of state that components can subscribe to. Selectors transform this state either synchronously or asynchronously
-
Atoms
Atoms are units of state. They're updateable and subscribeable: when an atom is updated, each subscribed component is re-rendered with the new value. They can be created at runtime, too. Atoms can be used in place of React local component state. If the same atom is used from multiple components, all those components share their state.
-
Selectors
A selector is a pure function that accepts atoms or other selectors as input. When these upstream atoms or selectors are updated, the selector function will be re-evaluated. Components can subscribe to selectors just like atoms, and will then be re-rendered when the selectors change.
Selectors are used to calculate derived data that is based on state. This lets us avoid redundant state, usually obviating the need for reducers to keep state in sync and valid. Instead, a minimal set of state is stored in atoms, while everything else is efficiently computed as a function of that minimal state. Since selectors keep track of what components need them and what state they depend on, they make this functional approach more efficient.
From the point of view of components, selectors and atoms have the same interface and can therefore be substituted for one another.
note TLDR Atom: Single value/state/object.
atom = x || { x: any } || []
Selector: Derived/Computed value based on an atom's current value or another selector value.
selector = someFunction(atom) || someFunction(anotherSelector)
Installation
yarn add recoil
Create an atom
Let's start with creating an atom which holds an input number. Since an atom is a global value, it requires a unique key.
import { atom } from 'recoil'
export const inputNumberAtom = atom({
key: 'inputNumberAtom',
default: 0,
})
Create selectors
Now we have an atom, let's create two selectors which uses the previously defined atom. Since selectors are global values, each one requires a unique key.
- addTenSelector : This function will increment the inputNumber by 10.
- squareOfAddTenSelector : This one demonstrates the usage of a
selector
inside aselector
. This will calculate the squared value of addTenSelector.
import { selector } from 'recoil'
import { inputNumberAtom } from './atom'
export const addTenSelector = selector({
key: 'addTenSelector',
get: ({ get }) => {
return get(inputNumberAtom) + 10
},
})
export const squareOfAddTenSelector = selector({
key: 'squareOfAddTenSelector',
get: ({ get }) => {
const addTenVal = get(addTenSelector)
return addTenVal * addTenVal
},
})
Final App
Now let's move on to creating our app using recoil
.
import React from 'react'
import { RecoilRoot, useRecoilState, useRecoilValue } from 'recoil'
import { inputNumberAtom } from './atom'
import { addTenSelector, squareOfAddTenSelector } from './selector'
const PrimaryComponent = () => {
const [inputNumber, setInputNumber] = useRecoilState(inputNumberAtom)
const addTenValue = useRecoilValue(addTenSelector)
const squaredValue = useRecoilValue(squareOfAddTenSelector)
const onChange = React.useCallback((event: any) => {
setInputNumber(event.target.value)
}, [])
return (
<div>
<h2>PrimaryComponent</h2>
<input type="number" onChange={onChange} value={inputNumber} />
<h4>inputNumber = {inputNumber}</h4>
<h4>addTenSelector = {addTenValue}</h4>
<h4>squareOfAddTenSelector = {squaredValue}</h4>
</div>
)
}
const SecondaryComponent = () => {
const inputNumber = useRecoilValue(inputNumberAtom)
const addTenValue = useRecoilValue(addTenSelector)
const squaredValue = useRecoilValue(squareOfAddTenSelector)
return (
<div>
<h2>SecondaryComponent</h2>
<h4>
Since recoil offers global state. This component will receive the state
updates as well{' '}
</h4>
<h4>inputNumber = {inputNumber}</h4>
<h4>addTenSelector = {addTenValue}</h4>
<h4>squareOfAddTenSelector = {squaredValue}</h4>
</div>
)
}
export default () => {
return (
<RecoilRoot>
<div>
<PrimaryComponent />
<hr />
<SecondaryComponent />
</div>
</RecoilRoot>
)
}
If you had followed this tutorial, now your app will look like this. Try changing the input number and see the selectors and atoms in action.!