
Security News
Rolldown Pulls Rust React Compiler Integration After Binary Size Increase
Rolldown paused Rust React Compiler integration after a 5MB binary size increase raised concerns about shipping React-specific code to all Vite users.
npm i resy
# yarn add resy
# pnpm add resy
import { createStore, useStore, ComponentWithStore } from "resy";
const store = createStore({ count: 0 });
// for hook component
function App() {
const { count } = useStore(store); // or store.useStore();
return (
<>
{count}
<button onClick={() => store.count++}>increase</button>
</>
);
}
// for class component
class AppClass extends ComponentWithStore {
store = this.connectStore(store);
render() {
const { count } = this.store;
return (
<>
{count}
<button onClick={() => { store.count++; }}>increase</button>
</>
);
}
}
resy requires the version of React v >= 16.8
| API | Description |
|---|---|
| createStore | Create a store container for state |
| useStore | Use state from the store container generated by createStore |
| setState | Update data |
| syncUpdate | Synchronously update data |
const demoStore1 = createStore({
count: 0,
text: "hello",
});
type DemoStateType = { count: number; text?: number | string };
// In this way, the type of text can be
// more accurately identified as number or string or undefined
const demoStore2 = createStore<DemoStateType>({
count: 0,
});
// This is a very important feature for retrieving the latest time or other data.
const demoStore3 = createStore(() => {
return {
count: 0,
time: Date.now(),
};
});
const demoStore4 = createStore({
count: 0,
increase() {
// this point store object, as follows example
// The updates and usage of these APIs will be detailed in subsequent chapters
this.count++;
// this.setState({ count: this.count + 1 });
// this.restore();
// demoStore4.count++;
// demoStore4.setState({ count: demoStore3.count + 1 });
},
});
import { createStore } from "resy";
type StateType = {
count: number;
text: string;
info: { name: string };
ageList: { age: number }[];
increase(): void;
inputValue?: string;
};
// The generated store can be shared globally
const store = createStore<StateType>({
count: 0,
text: "hello",
info: { name: "Jack" },
ageList: [{age: 12}, { age: 16 }],
increase() {
this.count++;
},
});
// Store such as login and theme can set unmountRestore to false
// so that it will not be reset globally.
const userStore = createStore<{ userName: string; userId: number }>(
{
userName: "wenmu",
userId: 0,
},
{
unmountRestore: false,
},
);
const themeStore = createStore<{ themeStyle: "dark" | "light" }>(
{
themeStyle: "dark",
},
{
unmountRestore: false,
},
);
import { useStore } from "resy";
function App() {
const { count, text } = useStore(store);
// or
// const { count, text } = store.useStore();
return (
<>
<p>{count}</p>
<p>{text}</p>
</>
);
}
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>
</>
);
}
import { useStore } from "resy";
function App() {
const state = store.useStore();
return (
<>
<p>{state.count}</p>
<p>{state.text}</p>
</>
);
}
import { useStore } from "resy";
function App() {
const {
count, text,
// The use of these api will be described in detail later.
setState, syncUpdate, restore, subscribe,
} = store.useStore();
return (
<>
<p>{count}</p>
<p>{text}</p>
</>
);
}
import { useStore } from "resy";
function App() {
const { count, text } = store.useStore();
// Updates can be assigned directly
function btn2() {
store.count++;
store.text = "456asd";
}
return (
<>
<p>{count}</p>
<p>{text}</p>
</>
);
}
import { ComponentWithStore, PureComponentWithStore } from "resy";
/**
* @description ComponentWithStore is inherited from React Component,
* PureComponentWithStore is inherited from React PureComponent;
*/
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>
</>
);
}
}
import { ComponentWithStore, createStore } from "resy";
/**
* @description The update methods of internal "this.userStore" and "this.themeStore"
* are the same as those of the connected store itself, and can be called directly.
*/
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>
</>
);
}
}
import { useStore } from "resy";
function App() {
const {
info: { name }, ageList, inputValue,
} = store.useStore();
function btn2() {
// store.info.name = "Jack"; // Invalid update
// store.ageList[0] = { age: 7 }; // Invalid update
store.info = { name: "Jack" }; // Effective update
store.ageList = [{age: 7}]; // Effective update
}
return (
<>
<p>{name}</p>
{ageList.map(item => `Age:${item}`)}<br/>
<button onClick={btn2}>btn2</button>
</>
);
}
import { createStore } from "@shein/resy";
const store = createStore({
count: 0,
text: "ok",
getTextPro(str?: number | string) {
/**
* @description Here, using `useStore` works fine even in class components,
* as we have made it compatible.
* In fact, if it's needed only in class components,
* you can directly use `this` to achieve the purpose of referencing data internally within the function for rendering.
* 🌟 The reason for using `this.useStore()` within `getTextPro` is because,
* in the demo below, the `App` component doesn't reference the `text` data within `store.useStore()`.
* Therefore, when updating the `text` state, if `useStore` isn't called inside `getTextPro`,
* it won't be possible to track the reference to the data for updates. Of course,
* this example is specific to hook components. As mentioned earlier,
* in class components, `this.useStore()` isn't actually necessary; you could directly use `this`.
* However, the usage of `this.useStore()` within `getTextPro` remains compatible even if `getTextPro` is placed inside a class component.
*/
const { text } = this.useStore();
// Returns the final result for rendering.
return `${str ?? ""}_${text}_world`;
},
});
const App = () => {
const { count, getTextPro } = store.useStore();
return (
<div>
<div>count:{count}</div>
<div>textPro:{getTextPro("none")}</div>
<button
onClick={() => {
store.count++;
}}
>
countChange
</button>
<button
onClick={() => {
store.text = "hello";
}}
>
textChange
</button>
</div>
);
};
import { createStore, ComponentWithStore } from "@shein/resy";
class App extends ComponentWithStore {
store = this.connectStore(store);
render() {
const { count, getTextPro } = this.store;
return (
<div>
<div>count:{count}</div>
<div>textPro:{getTextPro("none")}</div>
<button onClick={() => store.count++}>
countChange
</button>
<button onClick={() => store.text = "hello"}>
textChange
</button>
</div>
);
}
}
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>
</>
);
}
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>
);
}
the difference between the callback of setState and the callback of this.setState of class components
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>
</>
);
}
}
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>
);
}
import { useStore } from "resy";
const store = createStore({count: 0, text: "hello"});
function App() {
const { count, text } = store.useStore();
function btnClick1() {
store.setState(() => {
// Returns the object that will eventually be updated
// through the calculation of complex business logic
return {
count: count + 1,
text: "B-Way-setState-with-function",
};
});
}
function btnClick2() {
store.count = 9;
// The prevState parameter of the function
store.setState(prevState => {
console.log(prevState.count === 9); // true
console.log(store.count === 9); // true
return {
text: "ok",
};
});
}
return (
<>
<div>{count}</div>
<div>{text}</div>
<button onClick={btnClick1}>btn-1</button>
<button onClick={btnClick2}>btn-2</button>
</>
);
}
import { useStore, syncUpdate } from "resy";
/**
* @description 🌟 The main purpose of syncUpdate is to solve the problem
* that input box updates such as input cannot be updated in an asynchronous environment.
*/
function App() {
const { inputValue } = store.useStore();
function inputChange(event: React.ChangeEvent<HTMLInputElement>) {
store.syncUpdate({
inputValue: event.target.value,
});
// @example B
// store.syncUpdate(prevState => {
// // prevState is same as setState's prevState.
// return {
// inputValue: event.target.value,
// };
// });
// @example C
// You can also use the callback function
// store.syncUpdate({
// inputValue: event.target.value,
// }, nextState => {
// console.log(nextState);
// });
}
return (
<input value={inputValue} onChange={inputChange}/>
);
}
import { useStore } from "resy";
// Updates to count data will not cause Text components to re-render
function Text() {
const { text } = store.useStore();
return <p>{text}</p>;
}
// Updates to text data will not cause Count components to re-render
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>
</>
);
}
import { useStore, ComponentWithStore } from "resy";
// Updates to count data will not cause Text components to re-render
class TextClass extends ComponentWithStore {
store = this.connectStore(store);
render() {
const { text } = this.store;
return (
<p>{text}</p>
);
}
}
// Updates to text data will not cause Count components to re-render
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>
</>
);
}
}
| API | Description |
|---|---|
| useConciseState | Concise version of useState |
| subscribe | Subscribe for changes in store data generated by createStore |
| useSubscription | Hook of subscribe |
| restore | Restore data of store, with re-render effect |
| setOptions | Set the options parameter of createStore |
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>
</>
);
}
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>
</>
);
};
// You can also subscribe to a non-lifecycle data monitor directly.
const unsub = store.subscribe(() => {
// ... to do anything
}, ["count", "text"]);
// cancel subscirbe
// unsub();
store.subscribe(() => {
// ... to do anything
}, []);
// [] or no state keys is equal
// no state keys
store.subscribe(() => {
// ... to do anything
});
import { useEffect } from "react";
import { useStore } from "resy";
function App() {
const { count } = store.useStore();
// Here is an example of a function component.
// If it is a class component, it can be used in componentDidMount.
useEffect(() => {
/**
* @param listener: subscription monitoring callback function
* @param stateKeys: subscription listens for changes in certain data fields of a specific store.
* If empty, default listens for changes in any one of the data in store.
* @return Unsubscribe: unsubscribe to the function of listening
*/
const unsubscribe = store.subscribe(({
effectState, prevState, nextState,
}) => {
/**
* effectState:Currently changing data
* nextState:Data after change
* prevState:Data before change
*/
console.log(effectState, prevState, nextState);
}, ["count", "text"]);
// unsubscribe();
return () => {
unsubscribe();
// ... to do else anything
};
}, []);
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/>
</>
);
}
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>
</>
);
}
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>
);
}
MIT License (c) 刘善保
FAQs
React State Easy
The npm package resy receives a total of 5 weekly downloads. As such, resy popularity was classified as not popular.
We found that resy demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Rolldown paused Rust React Compiler integration after a 5MB binary size increase raised concerns about shipping React-specific code to all Vite users.

Security News
/Research
Mini Shai-Hulud expands into the Go ecosystem after hitting LeoPlatform npm packages and targeting GitHub Actions workflows.

Security News
The Fable shutdown shows how quickly model access can become a business continuity risk for AI-dependent engineering teams.