If Redux is simple, Recoil is simpler

Redux is relatively the most popular state management library in the react ecosystem. It is said to be built on flux mechanism with a simpler interface. In fact to further make redux simpler to its users, the redux team recently endorsed the redux/toolkit.

But is redux really simple? If I would provide the answer, my response is "NO" a thousand times. Let me share my story:

React has been amazing for me most especially during my early years of learning it. However, at a point in the learning stages, you would agree with me one starts to appreciate the importance to maintain states in a single file or component - what we call the single source of truth. Here Redux shows up. However, just like it is our savior, it also a bane. Somebody must have asked how?

Well redux destroyed the whole learning experience with React for me. It did so badly, I took a week off of learning react nor redux. The whole structure, how I make reactions (state and Eventhandlers), everything changed because of redux. I struggled. It was really horrible.

Few years into react, I found Recoil. Enough of react praise already. From ground up, recoil was built to behave like react. So if you include recoil in your project, there is no mental model shift like you did with learning redux, oops or perhaps like you will if you've not started learning redux. No boiler plates in Recoil, at least not compared to redux even with it acclaimed simple redux/toolkit. It does just what you want "maintain a state above all my components and make it reactish when it changes".

The most amazing part with recoil for me is how easy one can integrate it even as a react newbie.

In this tutorial, we would explore recoil building a github signin page clone. We would achieve this with the assistance of react and grommetux. Let's jump right in.

$: npx create-react-app gitup
$: cd gitup
$: cd src
$: ls
// you will find App.js App.test.js index.css
// index.js App.css and some three other files we don't care about here
$: mkdir components
$: cd components && touch gitup.js
$: cd ../..
$: code .
// open your root folder in visual studio code

Before you go any deeper, here is my
final result

Now you have an idea what we will cover. I hope you are exited about the fun.

Baby step: Using react setState

In this first part, we will achieve our clone with the popular React patterns - State, props and two-way flow.

Move into ./components/gitup.js

import React from "react";
import {GitBanner as Banner, GitCA as ExtraInfo} from "./static-utils";
import {Input, SubmitBtn} from "./reactish-utils";

import styled from "styled-components";
import {Card} from "grommet";

const {useState} = React;
const GithubSignup = () => {
    const [state, setState] = useState({
        username: "",
        password: "",
        done: false
    })

    const onSubmit = ()=> {
        setState({...state, done: true})
        return setTimeout(()=> {
        setState({...state, done: false})
        alert(state.username + " you're signed in")
    }, 2000)}
    const onChange = name => event => setState({...state, [name]: event.target.value})

    return <Styles>
    <div id="main">
        <Banner />
        <Card id="card">
            <form>
            <Input 
                For = "username"
                onChange={onChange("username")}
                st={state.username} />
            <Input 
                For= "password"
                onChange={onChange("password")}
                st={state.password} />
            <SubmitBtn 
                onSubmit={onSubmit}
                done={state.done} />
            </form>
        </Card>
        <ExtraInfo />
    </div>
    </Styles**>
};

const Styles = styled.div`
padding-top: 1.8rem;
display: flex;
div#main{
    flex-basis: 12rem;
    margin: 0 auto;
    display: flex;
    gap:1em;
    flex-direction: column;
    align-items: center;
    #card{
        background-color:#F6F8FA ;
        border-radius: 5px;
        padding:1rem;
         form{
            min-height: 12rem;
            display: flex;
            flex-direction: column;
            justify-content: space-around;
        }
    }
}
`

export default GithubSignup;

Well, that is enough to see our simple page clone.

Please, visit the repo, github.com/kelvinsekx/git-sign-page-and-rec.., for access to other components and their styling.

They are only stateless components, so nothing much to be bothered about

Just like we know already, our intention here is to achieve this same task with Recoil so as to take advantage of its scale and provide the changes to all our plenty imaginative children components.

Recoil first step: atom

Just like you did with useState above, we would move to recoil with atom. The only difference will be a key and a default property holding the initial state value.

const logInfo = atom({
// key that serves as an id to identify an atom
    key: "logInfo",
// your initial state value
    default: {
        username: "",
        password: "",
        done: false
    }
})

Usually what I do is that I keep all my recoil logic in a folder, recoilers. And put my atom logic in a file - stateManager.js. In our case, since it is a simple project, I will skip the folder part and put all recoil logics in a stateManager.js file

import {
    atom
} from 'recoil';

export const logInfo = atom({
//
})

Before I move on to using my shinny atom, let's have a small talk about atom.

Atom in recoiler

Atom is our source of truth where our state lives. It is so smart that it knows when changes are made to it. They behave like a wrap around our units state in order to make them reusable, updateable and subscribable. In our case it will be used together with another hook, useRecoilState instead of state and useState like we did initially. Atom is a function that takes an object as a parameter.

atom({})

The object however must be provided with some predefined properties to make atom work.

atom({
key: "String" /**first property*/,
default: "Any data structure [String, Object, Array]" /**second property*/
})

Properties:

  1. key: According to the docs, atom key is used for debug, persistence purpose and advanced API consumptions.
  2. Default: This is the default state value like it is passed to our useState case.

Take away

  • To read and write atom we use useRecoilState: this works like useState returning the read state and a setter function when called.
const [state, setter] = useRecoilState()
// for example the setter could have just been setState like it used below
const [state, setState] = useRecoilState()
  • To read only, which is my recommended approach, useRecoilValue is used. I will be ignoring this one for now.
const state = useRecoilValue()

Back to code

Since this is a baby step thing, we would quickly use the useRecoilState hook to start things out.

...
import {logInfo} from "./../stateManager.js"
import {useRecoilState} from "recoil"

// const {useState} = React;
const GithubSignup = () => {
   // kindly take note of this line
    const [state, setState] = useRecoilState(logInfo)

    ...

    return <Styles>
    <div id="main">
        ...
    </Styles**>
};

const Styles = ...

export default GithubSignup;

Yaaahi, and that's how simple it is. I only needed to replace useState with useRecoilState and pass an atom as its parameter. I didn't change anything else and my app just works fine.

Here is the updated code repo on github.

If you got here, Thanks for reading thus far and I hope this was helpful to see into how easy you can start with recoil. In my articles to come, I will be writing a more detailed and beginner friendly example with recoil. Stay tuned.