小巧、简单且强大的 React 状态管理
Tiny, easy and powerful React state management library [![PRs Welcome][badge-prwelcome]][prwelcome] [badge-prwelcome]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square [prwelcome]: http://makeapullrequest.com
Sponsored by:
・Tiny: Less than 1kb package to manage your state in React and Preact.
・Easy: You don't need actions, reducers, selectors, connect, providers, etc. Everything can be done in the simplest and most comfortable way.
・Powerful: When a store property is updated, only its components are re-rendered. It's not re-rendering components that use other store properties.
yarn add teaful
# or
npm install teaful --save
Each store has to be created with the createStore function. This function returns all the methods that you can use to consume and update the store properties.
import createStore from "teaful";
const { useStore } = createStore();
Or also with an initial store:
const initialStore = {
cart: { price: 0, items: [] },
};
const { useStore, getStore } = createStore(initialStore);
Or also with an event that is executed after every update:
const initialStore = {
cart: { price: 0, items: [] },
};
function onAfterUpdate({ store, prevStore }) {
console.log("This callback is executed after an update");
}
const { useStore } = createStore(initialStore, onAfterUpdate);
Input:
| name | type | required | description |
|---|---|---|---|
initialStore |
object |
false |
Object with your initial store. |
onAfterUpdate |
function |
false |
Function that is executed after each property change. More details. |
Output:
| name | type | description | example |
|---|---|---|---|
useStore |
Proxy |
Proxy hook to consume and update store properties inside your components. Each time the value changes, the component is rendered again with the new value. More info. | const [price, setPrice] = useStore.cart.price() |
getStore |
Proxy |
Similar to useStore but without subscription. You can use it as a helper outside (or inside) components. Note that if the value changes, it does not cause a rerender. More info. |
const [price, setPrice] = getStore.cart.price() |
setStore |
Proxy |
It's a proxy helper to modify a store property outside (or inside) components. More info. | setStore.user.name('Aral') or setStore.cart.price(price => price + 10) |
withStore |
Proxy |
HoC with useStore inside. Useful for components that are not functional. More info. |
withStore.cart.price(MyComponent) |
We recommend using this type of export:
// ✅
export const { useStore, getStore, withStore } = createStore({
cart: { price: 0, items: [] },
});
This way you can import it with:
// ✅
import { useStore } from '../store'
Avoid using a default export with all:
// ❌
export default createStore({ cart: { price: 0, items: [] } });
Because then you won't be able to do this:
// ❌ It's not working well with proxies
import { useStore } from '../store'
It's recommended to use the useStore hook as a proxy to indicate exactly what portion of the store you want. This way you only subscribe to this part of the store avoiding unnecessary re-renders.
import createStore from "teaful";
const { useStore } = createStore({
username: "Aral",
count: 0,
age: 31,
cart: {
price: 0,
items: [],
},
});
function Example() {
const [username, setUsername] = useStore.username();
const [cartPrice, setCartPrice] = useStore.cart.price();
return (
<>
setUsername("AnotherUserName")}>
Update {username}
setCartPrice((v) => v + 1)}>
Increment price: {cartPrice}€
);
}
However, it's also possible to use the useStore hook to use all the store.
function Example() {
const [store, setStore] = useStore();
return (
<>
setStore((s) => ({
...s,
username: "AnotherUserName",
}))
}
>
Update {store.username}
setStore((s) => ({
...s,
cart: { ...s.cart, price: s.cart.price + 1 },
}))
}
>
Increment price: {store.cart.price}€
);
}
Input:
| name | type | description | example |
|---|---|---|---|
| Initial value | any |
This parameter is not mandatory. It only makes sense for new store properties that have not been defined before within the createStore. If the value has already been initialized inside the createStore this parameter has no effect. |
const [price, setPrice] = useStore.cart.price(0) |
| event after an update | function |
This parameter is not mandatory. Adds an event that is executed every time there is a change inside the indicated store portion. | const [price, setPrice] = useStore.cart.price(0, onAfterUpdate) |
|
Output:
Is an Array with 2 items:
| name | type | description | example |
|---|---|---|---|
| value | any |
The value of the store portion indicated with the proxy. | A store portion |
| All store: |
|
| update value | function | Function to update the store property indicated with the proxy. | Updating a store portion:
Way 1:
Way 1:
Updating all store: Way 1: Way 1: |
Useful helper to modify the store from anywhere (outside/inside components).
Example:
const initialStore = { count: 0, name: 'Aral' }
const { setStore } = createStore(initialStore);
const resetStore = () => setStore(initialStore);
const resetCount = () => setStore.count(initialStore.count);
const resetName = () => setStore.name(initialStore.name);
// Component without any re-render (without useStore hook)
function Resets() {
return (
<>
Reset store
Reset count
Reset name
);
}
Another example:
const { useStore, setStore } = createStore({
firstName: '',
lastName: ''
});
function ExampleOfForm() {
const [formFields] = useStore()
return Object.entries(formFields).map(([key, value]) => (
{
// Update depending the key attribute
setStore[key](e.target.value)
}}
/>
))
}
This second example only causes re-renders in the components that consume the property that has been modified.
In this way:
const [formFields, setFormFields] = useStore()
// ...
setFormFields(s => ({ ...s, [key]: e.target.value })) // ❌
This causes a re-render on all components that are consuming any of the form properties, instead of just the one that has been updated. So using the setStore proxy helper is more recommended.
It works exactly like useStore but with some differences:
It does not make a subscription. So it is no longer a hook and you can use it as a helper wherever you want.
It's not possible to register events that are executed after a change.
getStore.cart.price(0, onAfterPriceChange); // ❌
function onAfterPriceChange({ store, prevStore }) {
// ...
}
createStore:const { getStore } = createStore(initialStore, onAfterUpdate); // ✅
function onAfterUpdat
暂无开放 Issues,或尚未同步最近议题。