hookex
v0.0.22
Published
A simple state manager for React
Downloads
37
Maintainers
Readme
hookex
A state manager for React without reducer, Provider, dispatcher etc. Design for large projects which has thousand of components.
- No Provider needed
- No Store needed
- No Reducer needed
- No Action Creator needed
- No Dispatcher needed
- Simple concept: State & Action
- Support Simple State (Synchronous State)
- Support Asynchronous State (with debouncing)
- 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"));