reaxn

Hooks for managing, dispatching, caching async actions

Usage no npm install needed!

<script type="module">
  import reaxn from 'https://cdn.skypack.dev/reaxn';
</script>

README

reaxn

Installation

Using npm

npm i reaxn --save

Using yarn

yarn add reaxn

Usages

Counter App

import React from "react";
import { render } from "react-dom";
import { createController, useAction, Provider } from "reaxn";

const controller = createController({
  state: { count: 1 },
});

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

const GetCount = ({ store, invalidate }) => {
  // GetCount action will be invalidated whenever store.count changed
  invalidate("store.count");
  return store.count;
};

const GetDoubleCount = ({ call }) => {
  const count = call("count", GetCount);
  return count * 2;
};

const IncreaseCount = ({ store }) => {
  // when store.count changed, the controller will emit store.count event
  store.count++;
};

const IncreaseCountAsync = async ({ call }) => {
  // delay in 1s
  await delay(1000);
  // then call IncreaseCount action
  call(IncreaseCount);
};

const App = () => {
  // call GetCount and retrieve its result
  const [{ result: count }] = useAction("count", GetCount);
  const [{ result: doubleCount }] = useAction("doubleCount", GetDoubleCount);
  // with lazy = true, no action called, just return action invoker
  const [, increase] = useAction({ action: IncreaseCount, lazy: true });
  // retrieve loading status when action is invoking
  const [{ loading }, increaseAsync] = useAction({
    action: IncreaseCountAsync,
    lazy: true,
  });

  return (
    <>
      <h1>Count: {count}</h1>
      <h1>Count x 2: {doubleCount}</h1>
      <button onClick={() => increase()}>Increase</button>
      <button onClick={() => increaseAsync()} disabled={loading}>
        {loading ? "Increasing...." : "Increase Async"}
      </button>
    </>
  );
};

render(
  <Provider controller={controller}>
    <App />
  </Provider>,
  document.getElementById("root")
);

User Management App

import React, { useRef } from "react";
import { render } from "react-dom";
import { createController, useAction, Provider } from "reaxn";

const controller = createController({});

// get all users, no invalidate condition
const GetUsers = () => {
  return fetch("https://jsonplaceholder.typicode.com/users").then((x) =>
    x.json()
  );
};

const AddUser = ({ update }, user) => {
  user = {
    ...user,
    id: Math.floor(Math.random() * 10000),
  };
  fetch({
    url: "https://jsonplaceholder.typicode.com/users",
    method: "POST",
    body: JSON.stringify(user),
    headers: {
      "Content-type": "application/json; charset=UTF-8",
    },
  });
  // perform optimistic update
  update("users", GetUsers, (users) =>
    // insert new user at the beginning of the list
    [user].concat(users)
  );
};

const App = () => {
  const [{ result: users, loading }] = useAction("users", GetUsers);
  const [, addUser] = useAction({ action: AddUser, lazy: true });
  const nameRef = useRef();
  const usernameRef = useRef();
  const emailRef = useRef();

  const handleSubmit = (e) => {
    e.preventDefault();
    addUser({
      name: nameRef.current.value,
      username: usernameRef.current.value,
      email: emailRef.current.value,
    });
    // clear inputs
    nameRef.current.value = "";
    usernameRef.current.value = "";
    emailRef.current.value = "";
    nameRef.current.focus();
  };

  return (
    <>
      <form onSubmit={handleSubmit}>
        <h2>Add New User</h2>
        <input ref={nameRef} placeholder="Name" />
        <br />
        <input ref={usernameRef} placeholder="Username" />
        <br />
        <input ref={emailRef} placeholder="Email" />
        <br />
        <button type="submit">Submit</button>
      </form>
      <xmp>
        {loading
          ? "Loading all users..."
          : JSON.stringify(
              users.map(({ id, name, username, email }) => ({
                id,
                name,
                username,
                email,
              })),
              null,
              2
            )}
      </xmp>
    </>
  );
};

render(
  <Provider controller={controller}>
    <App />
  </Provider>,
  document.getElementById("root")
);