npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-hooka

v2.0.0

Published

Manages global state of React application. Implemented around react-hooks, optimized for both synchronous and asynchronous global-state update operation.

Downloads

13

Readme

react-hooka


Global state management based on React-hooks, with built-in support for both synchronous & queued-asynchronus (FIFO) actions.


Table of Contents

Introduction:

"react-hooka" is React-hook based state management library. "react-hooka" supports both synchronous & queued-asynchronous (FIFO) actions.

All asynchronous state update operations are guaranteed to change the application state in the order of asynchronous-operation invocation.

The "react-hooka" is designed to have multiple independent slices within the global store. Each slice is an independent sandbox which contain "state" as well as "actions". Operations performed in context of a given "slice" does not affect other "slices" within the store.

React-components which subscribes to a given "slice" are only re-rendered when the subscribed slice-state gets any update. React-components using a slice have option to optout re-renders on slice-state updates if the need be.

Install:

npm install react-hooka

Features, Instructions and Glossary:

Store:

Store refers to a global object holding application state and available action on the state in a React application.

Under the hood, store maintains multiple slices, and each of the slice independently manages its state using the actions available on its state.

Slice:

Slice is an independent subset of the store. A slice holds a subset of application state and defined actions on its state.

Any operation in context of a given slice does not impact other slices within the store. Only the components using (subscribed to) a given slice are notified of the state updates and may perform rerender if required.

Actions:

Actions are methods responsible to update the state of a slice. Each action is identified by an "Action-Identifier" which must be unique string value within the slice.

Actions methods receives "currentState" as parameter which represents the current snapshot of slice-state.

Actions must return "updatedState". The state returned by the Action is replaced into the slice-state by the "react-hooka".

Note - v2.0.0: State returned by the Action is replaced, not merged into the existing state.

Actions are of two kinds:

  1. Synchronous Actions
  2. Asynchronous Actions

Synchronous Actions:

Synchronous action performs state update in synchronous fashion and are blocking in nature. All state update operations which do not perform I/O or Network operations are cadidates of synchronous actions.

// Synchronous Action named "SYNCHRONUS_ACTION_IDENTIFIER"
{
SYNCHRONUS_ACTION_IDENTIFIER: (currentState) => {
      try {
        const updatedState = {...currentState, n1: 100 };
        return updatedState;
      } catch (error) {
        throw new Error(error.message);
      }
    }
}

Asynchronous Actions:

Asynchronous action performs state update in asynchronous fashion and are non-blocking in nature. All Asynchronous operations are expected to return a "promise". All state update operations which perform I/O or Network oprations are cadidates of asynchronous actions.

// Asynchronous Action named "ASYNCHRONUS_ACTION_IDENTIFIER"
{
    ASYNCHRONUS_ACTION_IDENTIFIER: (currentState) => {
      return new Promise((resolve, reject) => {
        (async () => {
          try {
            setTimeout(() => {
              const updatedState = {...currentState, n1: 100 };
              resolve(updatedState);
            }, 100);
          } catch (error) {
            reject(new Error(error.message));
          }
        })();
      });
    }
}

Using react-hooka:

Using react-hooka is straight forward, which requires following steps:

Initialise store-slice (state and actions belonging to a slice) using "initStoreSlice" method.

"initStoreSlice" method takes 3 parameters:

  1. sliceIdentifier: Apllication wide unique string value to identify store-slice.
  2. actions: An object holding all the "actions" to be performed on the slice-state. All actions within the "actions" object need to have string "Action-Identifier" which should be unique within the slice.
  3. initialState: An object having initial state of the slice.
import { initStoreSlice } from "react-hooka";

  const actions = {
  SYNCHRONUS_ACTION_IDENTIFIER: (currentState) => {
    try {
      const updatedState = {...currentState, n1: 100 };
      return updatedState;
    } catch (error) {
      throw new Error(error.message);
    }
  },
  ASYNCHRONUS_ACTION_IDENTIFIER: (currentState) => {
    return new Promise((resolve, reject) => {
      (async () => {
        try {
          setTimeout(() => {
            const updatedState = {...currentState, n2: 200 };
            resolve(updatedState);
          }, 100);
        } catch (error) {
          reject(new Error(error.message));
        }
      })();
    });
  }
};

const initialState = {
    n1: 10,
    n2: 20,
    n3: 30
  };

initStoreSlice("TEST_STORE", actions, initialState);

Use "react-hooka" in React component for state management.

  1. Import useStore hook from "react-hooka".
import { useStore } from "react-hooka";
  1. Use the useStore hook with the slice-name of the slice in context.
const {state, dispatch, dispatchAsync} = useStore("TEST_STORE");

state represents the current state of the slice-state.

dispatch method can be used to perform synchronous (blocking) operation on slice-state.

dispatchAsync method can be used to perform asynchronous (non-blocking) operation on slice-state.

OPTIONAL OPTOUT from rerendering of React-components on slice-state updates.

There might be cases when React-components are only performing actions but not displaying the state (such as a FORM only adding new items to state but not displaying existing items.). In such cases React-components can optout rerenders by passing "false" to the 2nd parameter shouldTriggerRerender of useStore hook.

By default, value of shouldTriggerRerender is true, thus all slice-state updates will trigger rerender on React-components which are using useStore hook.

const { state, dispatch, dispatchAsync } = useStore("TEST_STORE", false);

OR

// Not using "state" at all.
const { dispatch, dispatchAsync } = useStore("TEST_STORE", false);

Delete a store-slice if needed.

If a store-slice is no more needed, it can be deleted from the global-store. deleteStoreSlice method of "react-hooka" can be used to delete a store-slice.

import { deleteStoreSlice } from "react-hooka";

deleteStoreSlice("SLICE_NAME");

Example:

1. Configure store slice:

// FILE: testStore.js

import { initStoreSlice } from "react-hooka";

const init = () => {
  const actions = {
    UPDATE1: (currentState) => {
      try {
        const updatedState = {...currentState, n1: 100 };
        return updatedState;
      } catch (error) {
        throw new Error(error.message);
      }
    },

    UPDATE2: (currentState) => {
      try {
        const updatedState = {...currentState, n2: 200 };
        return updatedState;
      } catch (error) {
        throw new Error(error.message);
      }
    },

    UPDATE3: (currentState) => {
      try {
        const updatedState = {...currentState, n3: 300 };
        return updatedState;
      } catch (error) {
        throw new Error(error.message);
      }
    },

    UPDATE1_ASYNC: (currentState) => {
      return new Promise((resolve, reject) => {
        (async () => {
          try {
            setTimeout(() => {
              const updatedState = {...currentState, n1: 1000 };
              resolve(updatedState);
            }, 100);
          } catch (error) {
            reject(new Error(error.message));
          }
        })();
      });
    },

    UPDATE2_ASYNC: (currentState) => {
      return new Promise((resolve, reject) => {
        (async () => {
          try {
            setTimeout(() => {
              const updatedState = {...currentState, n2: 2000 };
              resolve(updatedState);
            }, 500);
          } catch (error) {
            reject(new Error(error.message));
          }
        })();
      });
    },

    UPDATE3_ASYNC: (currentState) => {
      return new Promise((resolve, reject) => {
        (async () => {
          try {
            setTimeout(() => {
              const updatedState = {...currentState, n3: 3000 };
              resolve(updatedState);
            }, 800);
          } catch (error) {
            reject(new Error(error.message));
          }
        })();
      });
    }
  };

  const initialState = {
    n1: 10,
    n2: 20,
    n3: 30
  };

  initStoreSlice("TEST_STORE", actions, initialState);
};

export default init;

2. Initialize store slice:

// FILE: index.js

import React from "react";
import ReactDOM from "react-dom";

import App from "./App";
import initTestStore from "./store/testStore";

// Initialising "TEST_STORE"
initTestStore();

const rootElement = document.getElementById("root");
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  rootElement
);

3. Managing state in component:

// FILE: App.js

import React from "react";
import { useStore } from "react-hooka";
import "./styles.css";

export default function App() {
  const { state, dispatch, dispatchAsync } = useStore("TEST_STORE");
  
  const SyncUpdate = () => {
    dispatch("UPDATE1");
    dispatch("UPDATE2");
    dispatch("UPDATE3");
  };

  const AsyncUpdate = () => {
    dispatchAsync("UPDATE1_ASYNC");
    dispatchAsync("UPDATE2_ASYNC");
    dispatchAsync("UPDATE3_ASYNC");
  };

  return (
    <div className="App">
      <p>1. {state.n1}</p>
      <p>2. {state.n2}</p>
      <p>3. {state.n3}</p>
      <p><button onClick={SyncUpdate}>Synchronous Update</button></p>
      <p><button onClick={AsyncUpdate}>Asynchronous Update</button></p>
    </div>
  );
}

Live Examples:

  1. State update with Synchronous and Asynchronous actions - https://codesandbox.io/s/global-statehook-sync-async-actions-rkipy
  2. Optout component rerender on state update - https://codesandbox.io/s/global-statehook-optout-rerender-zuvo6
  3. Using multiple state-slices on one component - https://codesandbox.io/s/global-statehook-multiple-state-slices-9nec4?file=/src/index.js
  4. Using multiple state-slices through multiple components - https://codesandbox.io/s/global-statehook-multiple-state-slices-multiple-components-x7e98?file=/src/App.js

Author:

Abhay Kumar

License:

This project is licensed under the MIT License.