hookex

0.0.22 • Public • Published

hookex

A state manager for React without reducer, Provider, dispatcher etc. Design for large projects which has thousand of components.

  1. No Provider needed
  2. No Store needed
  3. No Reducer needed
  4. No Action Creator needed
  5. No Dispatcher needed
  6. Simple concept: State & Action
  7. Support Simple State (Synchronous State)
  8. Support Asynchronous State (with debouncing)
  9. Support Dynamic State

Samples

Counter App

import React from "react";
import { render } from "react-dom";
import { createState, createAction, useStates } from "hookex";
 
// define CountState with 1 as default value
const CountState = createState(1);
// define an action, specified CountState as dependencies
// action body receives CountState accessor
// using count() to get current state value and count(newValue) to update state
const Increase = createAction([CountState], count => count(count() + 1));
 
function App() {
  const [count] = useStates(CountState);
  return (
    <div>
      Counter: {count}
      <button onClick={Increase}>Click to increase counter</button>
    </div>
  );
}
 
render(<App />, document.getElementById("root"));

Dynamic State

Create dynamic state which is computed from other states

import React from "react";
import { render } from "react-dom";
import { createState, createAction, useStates } from "hookex";
 
const CountState = createState(1);
const DoubleCountState = createState([CountState], count => count * 2, { sync: true });
const Increase = createAction([CountState], count => count(count() + 1));
 
function App() {
  const [count, doubleCount] = useStates(CountState, DoubleCountState);
  return (
    <div>
      <p>Counter: {count}</p>
      <p>Double Counter: {doubleCount}</p>
      <button onClick={Increase}>Click to increase counter</button>
    </div>
  );
}
 
render(<App />, document.getElementById("root"));

Async State

Search github user

import React from "react";
import { render } from "react-dom";
import { createState, createAction, useStates } from "hookex";
 
const apiUrl = "https://api.github.com/users/";
const SearchTermState = createState("");
const UpdateSearchTerm = createAction([SearchTermState], (searchTerm, value) =>
  searchTerm(value)
);
// once searchTerm changed, UserInfo state will be recomputed
const UserInfoState = createState([SearchTermState], async searchTerm => {
  const res = await fetch(apiUrl + searchTerm);
  return await res.json();
});
 
function App() {
  const [searchTerm, userInfo] = useStates(SearchTermState, UserInfoState);
  const { value, done } = userInfo;
  return (
    <div>
      <input
        type="text"
        value={searchTerm}
        onChange={e => UpdateSearchTerm(e.target.value)}
      />
      <pre>{done ? JSON.stringify(value, null, 2) : "Searching..."}</pre>
    </div>
  );
}
 
render(<App />, document.getElementById("root"));

Using AsyncRender component

AsyncRender component receives specified async state (or multiple states). When state loaded, render callback/component will be called unless AsyncRender's children will be rendered instead

import React from "react";
import { render } from "react-dom";
import { createState, createAction, useStates, AsyncRender } from "./hookex";
 
const apiUrl = "https://api.github.com/users/";
const SearchTermState = createState("");
const UpdateSearchTerm = createAction([SearchTermState], (searchTerm, value) =>
  searchTerm(value)
);
const UserInfoState = createState([SearchTermState], async searchTerm => {
  const res = await fetch(apiUrl + searchTerm);
  return await res.json();
});
 
function UserInfo({ data }) {
  return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
 
function App() {
  const [searchTerm] = useStates(SearchTermState);
 
  return (
    <div>
      <p>
        <input
          type="text"
          value={searchTerm}
          onChange={e => UpdateSearchTerm(e.target.value)}
        />
      </p>
      <AsyncRender render={UserInfo} state={UserInfoState}>
        Loading...
      </AsyncRender>
    </div>
  );
}
 
render(<App />, document.getElementById("root"));

Saving and loading states with localStorage

import { createState, createAction, persist } from "hookex";
 
const CounterState = createState(1);
const Increase = createAction([CounterState], async counter =>
  console.log(counter(counter() + 1))
);
 
setInterval(Increase, 3000);
 
persist(
  {
    counter: CounterState
  },
  JSON.parse(localStorage.getItem("counterApp")) || {},
  state => localStorage.setItem("counterApp", JSON.stringify(state))
);

Update single state

Note: Cannot update computed state

import { createState } from "hookex";
 
const CounterState = createState(1);
 
setInterval(
  () =>
    CounterState(prev => {
      console.log(prev);
      return prev + 1;
    }),
  3000
);

Using State as event handler

You can pass state to element event, it can process input synthetic event (event.target.value/event.target.checked)

import React from "react";
import { render } from "react-dom";
import { createState, useStates } from "hookex";
 
const ValueState = createState("Hello world !!!");
const CheckedState = createState(true);
 
function App() {
  const [value, checked] = useStates(ValueState, CheckedState);
 
  return (
    <>
      <p>
        <input value={value} onChange={ValueState} />
      </p>
      <p>{value}</p>
      <p>
        <input type="checkbox" checked={checked} onChange={CheckedState} />
      </p>
      <p>{checked ? "checked" : "unchecked"}</p>
    </>
  );
}
 
render(<App />, document.getElementById("root"));

Package Sidebar

Install

npm i hookex

Weekly Downloads

3

Version

0.0.22

License

ISC

Unpacked Size

26 kB

Total Files

6

Last publish

Collaborators

  • linq2js