GitHub - aralroca/fragstore: Tiny (~800 B), easy and simple library to manage yo...
source link: https://github.com/aralroca/fragstore
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Fragstore
Tiny (~800 B), easy and simple (P)React state management library
After a store update -> only components that use the updated property are rendered.
Getting started:
Install it with Yarn:
yarn add fragstore
Or install it with Npm:
npm install fragstore --save
Usage:
Store
The Store
is an optional component where you can send the same parameters than the createStore
. Sometimes can be useful, for example, when the initial store is defined by an API, or also if you want to change some component state (not store) after some callback.
import createStore from "fragstore"; const { Store } = createStore(); function App() { return ( <Store store={{ username: "Aral", age: 31, }}> {/* rest */} </Store> ); }
Fragmented store (meaning of Fragstore)
The power of this library is that you can use fragmented parts of the store, so if a component uses only one field of the store, it will only re-render again if there is a change in this particular field and it will not render again if the other fields change.
import createStore from "fragstore"; const { useStore } = createStore({ username: "Aral", age: 31, cart: { price: 0, items: [] } }); function FragmentedExample() { const [username, setUsername] = useStore.username(); const [cartPrice, setCartPrice] = useStore.cart.price(); return ( <> <button onClick={() => setUsername("AnotherUserName")}> Update {username} </button> <button onClick={() => setCartPrice(v => v + 1)}> Increment price: {cartPrice}€ </button> </> ); }
Unfragmented store
The advantage of this library is to use the store in a fragmented way. Even so, there are cases when we want to reset the whole store or do more complex things. For these cases, we can use the hook useStore
directly.
import createStore from "fragstore"; const { useStore } = createStore({ username: "Aral", age: 31 }); function UnfragmentedExample() { const [store, update] = useStore(); return ( <> <h1>{state.username}, {state.age}</h1> <button onClick={() => update({ age: 32, cart: { price: 0, items: [] } })} > Update store </button> </> ); }
Adding new properties to the store
There are 3 ways to add a new property to the store:
1. Adding a new property on the Store
import createStore from "fragstore"; const { Store } = createStore({ username: "Aral" }); function App() { return <Store store={{ count: 0 }}>{/* rest */}</Store>; }
2. Using the useStore
to consume to a new property
const { Store } = createStore({ username: "Aral" }); // ... const [newProp, setNewProp] = useStore.newProp("Initial value of newProp") const [anotherProp, setAnotherProp] = useStore.anotherProp() // ... setAnotherProp("Initial value of anotherProp") setNewProp("Next value of newProp")
The hook argument works to define the initial value. It doesn't work when the initial value is already defined in createStore
or Store
:
const { Store } = createStore({ username: "Aral" }); // ... const [username, setUsername] = useStore.username("Another name") console.log(username) // -> Aral
In this case, if you want to update the value you should use the setUsername
method.
3. Using all the store with useStore
directly (not recommended)
const [store, setStore] = useStore() // ... setStore({ newProp: "I'm a new property", anotherProp: "I'm another new property" })
The problem with using the entire store is that the component will re-render whenever any property in the store is updated.
Callbacks
The second param of createStore
is callbacks Object<function>
. It's useful for example to fetch data to an endpoint after the state change, and roll back the state if the request fails. Callbacks can only be created at the first level. That is, if we have an update in cart.items[0].price
, only the callback of the cart { cart() {} }
will be called. However you receive the updated path
and you can implement the logic inside the callback:
Example:
const initialState = { quantity: 2, userName: 'Aral', age: 31, cart: { items: [{ name: 'example', price: 10 }] } } // Every callback is executed after a property change const callbacks = { quantity({ value, prevValue, updateValue }) { // Update quantity on API fetch('/api/quantity', { method: 'POST', body: value }) // Revert state change if it fails .catch(e => updateValue(prevValue)) }, age({ value, prevValue, updateValue }) { if (value > 100) { alert("Sorry, no more than 100 😜"); updateValue(prevValue); } }, cart({ path, value, prevValue, updateValue }) { if (path === "cart.items.0" && value.price > 10) { alert(`Price of ${value.name} should be lower than 10`); updateValue(prevValue); } } } const { Store, useQuantity } = createStore(initialState, callbacks)
Also you can overwrite or define callbacks on the Store
:
<Store store={{ newProperty: 'Another value'}} callbacks={{ newProperty(value) { console.log(value) } }}>
Example
import createStore from "fragstore"; const { Store, useStore } = createStore({ username: "Aral", age: 31, cart: { price: 0, items: [], } }); export default function App() { return ( <Store> <AllStore /> <Username /> <CartPrice /> <CartFirstItem /> <Age /> <NewProperty /> </Store> ); } function AllStore() { const [store, update] = useStore(); console.log({ store }); // all store return ( <button onClick={() => update({ age: 31, username: "Aral" })}> Reset </button> ); } function Username() { const [username, setUsername, resetUsername] = useStore.username(); return ( <> <h1>Username: {username}</h1> <button onClick={() => setUsername("Another name")}> Update username </button> <button onClick={resetUsername}> Reset username </button> </> ); } function CartPrice() { const [price, setPrice, resetPrice] = useStore.cart.price(); return ( <> <h1>Price: {price}€</h1> <button onClick={() => setPrice(v => v + 1)}> Inc price </button> <button onClick={resetPrice}> Reset username </button> </> ); } function CartFirstItem() { const [item, setItem, resetItem] = useStore.cart.items[0](); return ( <> <h1>Item: {JSON.stringify(item)}</h1> <button onClick={() => setItem({ name: "new Item" })}> Update item </button> <button onClick={resetItem}> Reset item </button> </> ); } function Age() { const [age, setAge, resetAge] = useStore.age(); console.log("render age", age); return ( <div> <div>{age}</div> <button onClick={() => setAge((s) => s + 1)}>Inc age</button> <button onClick={resetAge}>Reset age</button> </div> ); } function NewProperty() { const [newProperty, setNewProperty] = useStore.newProperty(); return ( <> { newProperty ? <div>{newProperty}</div> : ( <button onClick={() => setNewProperty("I'm a new property")}> Create new property </button> ) } </> ); }
Contributors
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK