Tough Actin' ReactN

joshtronic

Out of the box, React’s state management is limited to a component’s scope or at the very most, a shared state that requires wiring components together. This lack of a global state is why things like Redux and various other packages exist. New on the scene is ReactN which is an extension of React that comes with state management at a global scale baked right in.

Getting Started

To get started with ReactN, we will need to install it to our project:

# via npm
$ npm install reactn --save

# via Yarn
$ yarn add reactn

Of course, we’ll also need to import the library into our code. Because ReactN is an extension of React, you will swap out lines like:

import React,  { Component } from 'react';

With lines like:

import React,  { Component } from 'reactn';

Initializing State

There’s a bit more to it than just swapping react with reactn, we’ll also need to import the necessary state management methods, depending on if we’re initializing or leveraging the state and the type of component we’re utilizing, purely functional with Hooks or class components.

To initialize our state, we’ll want to use the setGlobal method and assign our initial state values before we render our application. If you’re familiar with Redux, then you’re already familiar with this concept:

import React, { setGlobal } from "reactn";
import { render } from "react-dom";
import GatorTrap from "./GatorTrap";

// Initialize our state:
setGlobal({
  baits: ["fish", "turtle", "coypu", "muskrat", "deer"],
  caught: 0
});

const GatorCatchinApp = () => {
  return (
    <div>
      <h1>Gator Catchin' App</h1>
      <GatorTrap />
      <GatorTrap />
      <GatorTrap />
      <GatorTrap />
      <GatorTrap />
    </div>
  );
}

const container = document.createElement("div");
document.body.appendChild(container);
render(<GatorCatchinApp />, container);

Reading from the Global State

Functional Components

The benefit of using functional components, and the newly production-ready Hooks, is that there is hardly any boilerplate involved.

To complement the setGlobal method we used earlier, there is a useGlobal method that works with very similar syntax to Hooks, but targeting the global state:

import React, { useGlobal, useState } from "reactn";

const GatorTrap = () => {
  // Reads our available bait from the global state
  const [baits] = useGlobal("baits");

  // Keeps track if we've baited the trap
  const [baited, setBaited] = useState(false);

  return (
    <div>
      {!baited && (
        <select
          onChange={event => {
            if (event.target.value) {
              setBaited(true);
            }
          }}
        >
          <option value="">Bait This Trap</option>
          {baits.map(bait => (
            <option key={bait} value={bait}>
              {bait}
            </option>
          ))}
        </select>
      )}
      {baited && <div>Trap baited... now we wait.</div>}
      <br />
      <br />
    </div>
  );
};

export default GatorTrap;

Class Components

With ReactN, class components actually benefit from having even less boilerplate as the global state is made readily available when importing React from reactn by way of a global object scoped to this.

Here’s how we’d rewrite our functional component as a globally aware class component:

import React, { Component } from "reactn";

class GatorTrap extends Component {
  constructor(props) {
    super(props);

    // Keeps track if we've baited the trap
    this.state = {
      baited: false,
    };
  }

  onChange = (event) => {
    if (event.target.value) {
      this.setState({ baited: true });
    }
  }

  render() {
    return (
      <div>
        {!this.state.baited && (
          <select onChange={this.onChange}>
            <option value="">Bait This Trap</option>
            {this.global.baits.map(bait => (
              <option key={bait} value={bait}>
                {bait}
              </option>
            ))}
          </select>
        )}
        {this.state.baited &&
          <div>Trap baited... now we wait.</div>
        }
        <br />
        <br />
      </div>
    );

  }
}

export default GatorTrap;

Writing to the Global State

Thus far, we only have utilized half of what the useGlobal method has to offer. In addition to allowing us to read from the global state, we can also use it to write to the global state.

Let’s take our previous GatorTrap component and update it to actually catch a gator! To do so, we’ll update our components to reset the baited traps and “catch” a gator after 10 seconds.

Functional Components

As mentioned before, the syntax is very similar to that of Hooks, so if you’re already using hooks, this should all feel very familiar.

import React, { useGlobal, useState } from "reactn";

const GatorTrap = () => {
  // Reads our available bait from the global state
  const [baits] = useGlobal("baits");
  const [caught, setCaught] = useGlobal("caught");

  // Keeps track if we've baited the trap
  const [baited, setBaited] = useState(false);

  if (baited) {
    setTimeout(() => {
      setBaited(false);
      setCaught(caught + 1);
    }, 10 * 1000);
  }

  return (
    <div>
      {!baited && (
        <select
          onChange={event => {
            if (event.target.value) {
              setBaited(true);
            }
          }}
        >
          <option value="">Bait This Trap</option>
          {baits.map(bait => (
            <option key={bait} value={bait}>
              {bait}
            </option>
          ))}
        </select>
      )}
      {baited && <div>Trap baited... now we wait.</div>}
      <br /><br />
    </div>
  );
};

export default GatorTrap;

Class Components

Similar to how the global state is automatically available on this, we can also call setGlobal in a similar fashion.

import React, { Component } from "reactn";

class GatorTrap extends Component {
  constructor(props) {
    super(props);

    // Keeps track if we've baited the trap
    this.state = {
      baited: false
    };
  }

  onChange = event => {
    if (event.target.value) {
      this.setState({ baited: true });

      setTimeout(() => {
        this.setState({ baited: false });
        this.setGlobal({ caught: this.global.caught + 1 });
      }, 10 * 1000);
    }
  };

  render() {
    return (
      <div>
        {!this.state.baited && (
          <select onChange={this.onChange}>
            <option value="">Bait This Trap</option>
            {this.global.baits.map(bait => (
              <option key={bait} value={bait}>
                {bait}
              </option>
            ))}
          </select>
        )}
        {this.state.baited &&
          <div>Trap baited... now we wait.</div>
        }
        <br /><br />
      </div>
    );
  }
}

export default GatorTrap;

Conclusion

There’s a lot to like about ReactN. It’s an extension of React so there’s not a ton to learn and it mirrors Hooks syntax quite a bit making it even more familiar. Outside of basic state management via setters and getters, there is also functionality to reset the state tree and convert the global state into props similar to Redux’s connect.

Overall, ReactN felt very lightweight in comparison to Redux and the familiar syntax made it a pleasure to work with. Even though it’s a fairly new package, it felt more than sufficient for basic state management in React.

Want to see these gator traps in action? Check out the code over on CodeSandbox.

Oh, and by the way, no alligators, “gator” or otherwise were harmed in the writing of these components :)

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#