DEV Community

loading...

Senko - easy global state in react

raghavmisra profile image Raghav Misra ・3 min read

Straightforward global state in React.

This project is a work-in-progress, so feel free to contribute. :D
Feedback much, much appreciated!

Why Senko?

When writing React apps, global state management becomes a larger concern than it should be. Enter Senko, an easy state management solution with a lot of power.

Let's look at a simple example:

import React from "react";
import senko from "senko";

const useStore = senko({ count: 0 });

function Counter() {
    const store = useStore();

    return <>
        <code>{store.count}</code>
        <button onClick={() => store.count++}>up</button>
        <button onClick={() => store.count--}>down</button>
    </>;
}
Enter fullscreen mode Exit fullscreen mode

The useStore hook that is returned from the senko(...) call can be called from any component, and they will all refer to the same state.

Features:

  • First-class Typescript support (like really first class).
  • Multiple senko calls can be used to make isolated stores that can then be used in any component.
  • Really straightforward, no top-level provider wrappers, etc.

Check it out!

Github | npm i senko

Let's build an example:

Scaffold an app with CRA

npx create-react-app senko-test --template=typescript
(feel free to follow along with JS instead)

Restructure files & folders

  1. Delete everything in /src
  2. Create the following files in /src:
    • index.tsx
    • store.ts

yarn add senko

No senko app is complete without senko!

Write the store

Inside store.ts, throw the following.
I've added comments to walk you through it.

// No senko app is complete without senko!
import senko from "senko";

// We're gonna have a signup form:
// Pass in the initial state to the senko function:
export const useStore = senko({
    username: "",
    email: "",
    password: ""
});

// Oh also you can use a default export instead,
// I'm just not a big fan xD.
Enter fullscreen mode Exit fullscreen mode

Write the frontend

Okay, now that the store is done, we can write the actual React code.

Here's a template so you don't need to write the small stuff:

import React from "react";
import ReactDOM from "react-dom";
import { useStore } from "./store";

function Form() {
}

ReactDOM.render(<Form />, document.querySelector("#root"));
Enter fullscreen mode Exit fullscreen mode

Now, we have the basic stuff in place, let's dive into writing the Form component.

function Form() {
    return (
        <form>
            <label>Username:</label>
            <input 
                type="text"
                placeholder="CoolGuy1234" 
            />

            <label>Email:</label>
            <input 
                type="email" 
                placeholder="coolguy1234@gmail.io" 
            />

            <label>Password:</label>
            <input 
                type="password"
                placeholder="Shhhhhhhhh!" 
            />

            <button type="submit">Signup!</button>
        </form>
    );
}
Enter fullscreen mode Exit fullscreen mode

There's our form structure (not a great-looking one, but it's there).

Two-way binding

Now let's look at binding these inputs to the store.

function Form() {
    const store = useStore(); // we imported this before

    /* omitted for brevity */
}
Enter fullscreen mode Exit fullscreen mode

Usually, a two-way binding would like this:

<input 
    value={store.username} 
    onInput={e => store.username = e.target.value} 
/>
Enter fullscreen mode Exit fullscreen mode

However, with a Senko store, you can use our two-way binding helper:

<input {...store.model.username()} />
Enter fullscreen mode Exit fullscreen mode

Basically use store.model.thePropYouWantToBindTo (in our case: username, email, and password).

These bindings in our Form component would look like:

function Form() {
    const store = useStore();

    return (
        <form>
            <label>Username:</label>
            <input 
                type="text"
                placeholder="CoolGuy1234" 
                {...store.model.username()}
            />

            <label>Email:</label>
            <input 
                type="email" 
                placeholder="coolguy1234@gmail.io" 
                {...store.model.email()}
            />

            <label>Password:</label>
            <input 
                type="password"
                placeholder="Shhhhhhhhh!" 
                {...store.model.password()}
            />

            <button type="submit">Signup!</button>
        </form>
    );
}
Enter fullscreen mode Exit fullscreen mode

Finishing up

How do we know this two-way binding actually works?

Let's add a submit event to our form and prove it!

function Form() {
    const store = useStore();

    const onLogin: React.FormEventHandler = (e) => {
        e.preventDefault();
        console.log(
            "You signed up with the username:",
            store.username,
            "\nThe email:",
            store.email,
            "\nAnd your password was supposed to be secret but we don't care:", 
            store.password
        );
    };

    return (
        <form onSubmit={onLogin}>
            {/* omitted for brevity */}
        </form>
    );
}
Enter fullscreen mode Exit fullscreen mode

Try it out

Keep adding different values to the inputs and hitting submit!
You should see updated values everytime.

Farewell!

Thanks for reading this far! :D

Hope you enjoyed this post, a reaction or feedback would be much appreciated.

https://github.com/raghav-misra/senko-demo

Discussion (0)

Forem Open with the Forem app