Features
- š Easy!!!
- š Support for class and hook
- š Better performance optimization
Install
npm i resy
Usage
import { createStore, useStore, ComponentWithStore } from "resy";
const store = createStore({ count: 0 });
function App() {
const { count } = useStore(store);
return (
<>
{count}
<button onClick={() => store.count++}>increase</button>
</>
);
}
class AppClass extends ComponentWithStore {
store = this.connectStore(store);
render() {
const { count } = this.store;
return (
<>
{count}
<button onClick={() => { store.count++; }}>increase</button>
</>
);
}
}

Basic API
resy requires the version of React v >= 16.8
| createStore | Create a store container for state |
| useStore | Use state from the store container generated by createStore |
| setState | Update data |
| syncUpdate | Synchronously update data |
Detailed introduction of api
createStore
the store returned by createStore can be shared globally
const demoStore1 = createStore({
count: 0,
text: "hello",
});
paradigm type
type DemoStateType = { count: number; text?: number | string };
const demoStore2 = createStore<DemoStateType>({
count: 0,
});
function return
const demoStore3 = createStore(() => {
return {
count: 0,
time: Date.now(),
};
});
initial function attribute
const demoStore4 = createStore({
count: 0,
increase() {
this.count++;
},
});
general use
import { createStore } from "resy";
type StateType = {
count: number;
text: string;
info: { name: string };
ageList: { age: number }[];
increase(): void;
inputValue?: string;
};
const store = createStore<StateType>({
count: 0,
text: "hello",
info: { name: "Jack" },
ageList: [{age: 12}, { age: 16 }],
increase() {
this.count++;
},
});
createStore options item - unmountRestore
const userStore = createStore<{ userName: string; userId: number }>(
{
userName: "wenmu",
userId: 0,
},
{
unmountRestore: false,
},
);
const themeStore = createStore<{ themeStyle: "dark" | "light" }>(
{
themeStyle: "dark",
},
{
unmountRestore: false,
},
);
useStore
deconstruction usage mode
import { useStore } from "resy";
function App() {
const { count, text } = useStore(store);
return (
<>
<p>{count}</p>
<p>{text}</p>
</>
);
}
Mixed use of store
import { useStore } from "resy";
function App() {
const { userName } = userStore.useStore();
const { themeStyle } = themeStore.useStore();
return (
<>
<p>{userName}</p>
<p>{themeStyle}</p>
<button onClick={() => { userStore.userName = "LF" }}>nameChange</button>
<button onClick={() => { themeStore.setState({ themeStyle: "light" }) }}>themeChange</button>
</>
);
}
direct read usage mode
import { useStore } from "resy";
function App() {
const state = store.useStore();
return (
<>
<p>{state.count}</p>
<p>{state.text}</p>
</>
);
}
The method of deconstructing StoreUtils
setState, syncUpdate, restore, subscribe,
the four methods of StoreUtils are setState, syncUpdate,
restore and subscribe, it can be deconstructed and used directly
from useStore, but store itself has these four methods,
which are described in more detail in the following sections.
import { useStore } from "resy";
function App() {
const {
count, text,
setState, syncUpdate, restore, subscribe,
} = store.useStore();
return (
<>
<p>{count}</p>
<p>{text}</p>
</>
);
}
direct assignment update
import { useStore } from "resy";
function App() {
const { count, text } = store.useStore();
function btn2() {
store.count++;
store.text = "456asd";
}
return (
<>
<p>{count}</p>
<p>{text}</p>
</>
);
}
Using in class components
ComponentWithStorećPureComponentWithStore
import { ComponentWithStore, PureComponentWithStore } from "resy";
class AppClass extends ComponentWithStore {
store = this.connectStore(store);
render() {
const { count } = this.store;
return (
<>
{count}
<button onClick={() => { store.count++; }}>button +</button>
</>
);
}
}
class PureAppClass extends PureComponentWithStore {
store = this.connectStore(store);
render() {
const { count } = this.store;
return (
<>
{count}
<button onClick={() => { store.count++; }}>button +</button>
</>
);
}
}
Mixed use of store
import { ComponentWithStore, createStore } from "resy";
class AppClass extends ComponentWithStore {
userStore = this.connectStore(userStore);
themeStore = this.connectStore(themeStore);
render() {
const { userName } = this.userStore;
const { theme } = this.themeStore;
return (
<>
<span>{userName}</span>
<span>{theme}</span>
<button onClick={() => { this.userStore.userName = "LD" }}>
nameChange
</button>
<button onClick={() => { this.themeStore.setState({ theme: "light" }) }}>
themeChange
</button>
</>
);
}
}
Invalid update
import { useStore } from "resy";
function App() {
const {
info: { name }, ageList, inputValue,
} = store.useStore();
function btn2() {
store.info = { name: "Jack" };
store.ageList = [{age: 7}];
}
return (
<>
<p>{name}</p>
{ageList.map(item => `Ageļ¼${item}`)}<br/>
<button onClick={btn2}>btn2</button>
</>
);
}
setState
import { useStore } from "resy";
function App() {
const { count, text } = store.useStore();
return (
<>
<div>{count}</div>
<div>{text}</div>
<button
onClick={() => {
store.setState({
text: "demo-setState",
count: count + 1,
});
}}
>
btn
</button>
</>
);
}
setState's callback
import { useStore } from "resy";
function App() {
const { text } = store.useStore();
return (
<button
onClick={() => {
store.setState({
text: "cur-text",
}, nextState => {
console.log(nextState.text === "cur-text"); // true
});
}}
>
{text}
</button>
);
}
parameters of callback for setState
the difference between the callback of setState
and the callback of this.setState of class components
- reading this.state in the callback function of this.setState
in the class component obtains the latest data in the current round of updates.
import { Component } from "react";
class TestClassX extends Component {
constructor() {
super();
this.state = { count: 0, text: "class-x" };
}
render() {
const { count, text } = this.state;
return (
<>
{count},{text}
<button
onClick={() => {
this.setState({
text: "Try",
}, () => {
console.log(this.state.count === 9); // true
});
this.setState({ count: 9 });
}}
>
btn
</button>
</>
);
}
}
- however, the nextState of the callback function
of resy's setState is the latest data in the current synchronization phase,
but it does not belong to the latest data after the final round of updates.
import { useStore, createStore } from "resy";
const store = createStore({count: 0, text: "hello"});
function App() {
const { text } = store.useStore();
return (
<button
onClick={() => {
store.setState({
text: "cur-text",
}, nextState => {
console.log(nextState.text === "cur-text"); // true
console.log(nextState.count === 0); // true
console.log(store.count === 9); // true
});
store.setState({count: 9});
}}
>
{text}
</button>
);
}
parameters of the function type of setState
import { useStore } from "resy";
const store = createStore({count: 0, text: "hello"});
function App() {
const { count, text } = store.useStore();
function btnClick1() {
store.setState(() => {
return {
count: count + 1,
text: "B-Way-setState-with-function",
};
});
}
function btnClick2() {
store.count = 9;
store.setState(prevState => {
console.log(prevState.count === 9);
console.log(store.count === 9);
return {
text: "ok",
};
});
}
return (
<>
<div>{count}</div>
<div>{text}</div>
<button onClick={btnClick1}>btn-1</button>
<button onClick={btnClick2}>btn-2</button>
</>
);
}
syncUpdate
import { useStore, syncUpdate } from "resy";
function App() {
const { inputValue } = store.useStore();
function inputChange(event: React.ChangeEvent<HTMLInputElement>) {
store.syncUpdate({
inputValue: event.target.value,
});
}
return (
<input value={inputValue} onChange={inputChange}/>
);
}
fine-grained of update
hook
import { useStore } from "resy";
function Text() {
const { text } = store.useStore();
return <p>{text}</p>;
}
function Count() {
const { count } = store.useStore();
return <p>{count}</p>;
}
function App() {
const { increase, name } = store.useStore();
return (
<>
<Text/>
<Count/>
<div>{name}</div>
<button onClick={() => { store.name = "app"; }}>btn-name</button>
<button onClick={increase}>btn+</button>
<button onClick={() => { store.count-- }}>btn-</button>
</>
);
}
class
import { useStore, ComponentWithStore } from "resy";
class TextClass extends ComponentWithStore {
store = this.connectStore(store);
render() {
const { text } = this.store;
return (
<p>{text}</p>
);
}
}
class CountClass extends ComponentWithStore {
store = this.connectStore(store);
render() {
const { count } = this.store;
return (
<p>{count}</p>
);
}
}
class AppClass extends ComponentWithStore {
store = this.connectStore(store);
render() {
const { increase, name } = this.store;
return (
<>
<Text/>
<Count/>
<div>{name}</div>
<button onClick={() => { store.name = "app" }}>btn-name</button>
<button onClick={increase}>btn+</button>
<button onClick={() => { store.count-- }}>btn-</button>
</>
);
}
}
Advanced API
| useConciseState | Concise version of useState |
| subscribe | Subscribe for changes in store data generated by createStore |
| useSubscription | Hook of subscribe |
| restore | Reset data of store, with re-render effect |
| setOptions | Set the options parameter of createStore |
Detailed introduction of api
useConciseState
The functionality of useConciseState is not limited to just a concise syntax on the surface.
Its deeper capability is to deconstruct the store and provide sub-components with a doorway
that allows for comprehensive control over the store's data, rendering, updates, and subscriptions.
import { useConciseState } from "resy";
const initialState = {
count: 123,
text: "hello-consice",
};
function App() {
const { count, text, store, setState } = useConciseState(initialState);
return (
<>
<div
onClick={() => {
setState({
count: count + 1,
text: "ASD",
});
// or
// store.count++;
// store.text = "ASD";
// or
// store.setState({
// count: count + 1,
// text: "ASD",
// });
// store has all the data of useConciseState
// and the restore, syncUpdate, and subscribe methods
}}
>
{count}
</div>
<div>{text}</div>
</>
);
}
restorećsyncUpdatećsubscribe these api can also be deconstructed and used directly.
import { useEffect } from "react";
import { useConciseState } from "resy";
function App() {
const { count, text, restore, syncUpdate, subscribe } = useConciseState(initialState);
useEffect(() => {
return subscribe(({ effectState }) => {
console.log(effectState);
}, ["text"]);
}, []);
return (
<>
<input
value={text}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
syncUpdate({text: event.target.value});
}}
/>
<div onClick={() => restore()}>reset-btn</div>
<div>{text}</div>
</>
);
}
Advantages of useConciseState
import { useConciseState, ConciseStoreHeart } from "resy";
type State = {
count: number;
text: string;
};
function ChildOne(props: ConciseStoreHeart<State>) {
const { store } = props;
const { count, text } = useStore(store);
return (
<>
<p>ChildOne-count:{count}</p>
<p>ChildOne-text:{text}</p>
<button
onClick={() => {
store.setState({
count: 999,
text: "ChildOneSetStateNewText",
});
}}
>
childOneBtn
</button>
</>
);
}
function ChildTwo(props: ConciseStoreHeart<State>) {
const { store } = props;
const [data, setData] = useState({ count: 0, text: "hello" });
store.useSubscription(({ nextState }) => {
setData(nextState);
});
return (
<>
<p>ChildTwo-count:{data.count}</p>
<p>ChildTwo-text:{data.text}</p>
</>
);
}
const App = () => {
const { count, text, store } = useConciseState<State>({
count: 0,
text: "hello",
});
return (
<>
<p>{count}</p>
<p>{text}</p>
<ChildOne store={store} />
<ChildTwo store={store} />
<button onClick={() => {
store.setState({
count: 1,
text: "world",
});
}}>change</button>
</>
);
};
subscribećuseSubscription
global subscribe
const unsub = store.subscribe(() => {
}, ["count", "text"]);
empty keys
empty state keys
You can also not add an array of monitoring subscription data keys,
Both empty keys and no keys mean listening subscriptions to changes in the entire store data.
store.subscribe(() => {
}, []);
store.subscribe(() => {
});
general use
import { useEffect } from "react";
import { useStore } from "resy";
function App() {
const { count } = store.useStore();
useEffect(() => {
const unsubscribe = store.subscribe(({
effectState, prevState, nextState,
}) => {
console.log(effectState, prevState, nextState);
}, ["count", "text"]);
return () => {
unsubscribe();
};
}, []);
function btnClickA() {
store.count++;
}
function btnClickB() {
store.text = "control btn-b click update text state value";
}
function btnClickC() {
store.setState({
count: count + 1,
text: "control btn-c click update text state value",
});
}
return (
<>
<p>{count}</p>
<button onClick={btnClickA}>btn-A</button><br/>
<button onClick={btnClickB}>btn-B</button><br/>
<button onClick={btnClickC}>btn-C</button>
</>
);
}
import { useEffect } from "react";
import { useStore, useSubscription } from "resy";
function App() {
const { count } = store.useStore();
useSubscription(store, ({
effectState, prevState, nextState,
}) => {
console.log(effectState, prevState, nextState);
}, ["count"]);
function btnClick() {
store.count++;
}
return (
<>
<p>{count}</p>
<button onClick={btnClick}>btn</button><br/>
</>
);
}
import { useEffect } from "react";
import { useStore } from "resy";
function App() {
const { count } = store.useStore();
store.useSubscription(({
effectState, prevState, nextState,
}) => {
console.log(effectState, prevState, nextState);
}, ["count"]);
function btnClick() {
store.count++;
}
return (
<>
<p>{count}</p>
<button onClick={btnClick}>btn</button><br/>
</>
);
}
restore
import { useStore } from "resy";
function App() {
const { count, text } = store.useStore();
return (
<>
<div>{count}-{text}</div>
<div
onClick={() => {
// data recover initial
store.restore();
// You can also add callback functions in the restore function
// store.restore(nextState => {
// console.log(nextState);
// });
}}
>
reset-btn
</div>
</>
);
}
import { createStore, useStore } from "resy";
const timeStore = createStore(() => {
return {
now: Date.now(),
};
});
function App() {
const { now } = useStore(timeStore);
return (
<>
<div>now:{now}</div>
<div
onClick={() => {
// time data now recover and also changed initial,
// because of initialState is function return.
store.restore();
}}
>
reset-btn
</div>
</>
);
}
setOptions
function App() {
return (
<button
onClick={() => {
// Use less scenes, use it with caution
// You can change the unmountRestore parameter setting of createStore
store.setOptions({ unmountRestore: false });
}}
>
btn
</button>
);
}
License
MIT License (c) ååäæ