{"id":13422405,"url":"https://github.com/teafuljs/teaful","last_synced_at":"2026-04-01T23:03:10.324Z","repository":{"id":39028852,"uuid":"346826188","full_name":"teafuljs/teaful","owner":"teafuljs","description":"🍵 Tiny, easy and powerful React state management","archived":false,"fork":false,"pushed_at":"2026-03-22T10:11:57.000Z","size":1062,"stargazers_count":713,"open_issues_count":18,"forks_count":23,"subscribers_count":9,"default_branch":"master","last_synced_at":"2026-03-22T20:03:04.385Z","etag":null,"topics":["easy","fragmented","javascript","management","performance","preact","react","state","state-management","store","teaful","tiny"],"latest_commit_sha":null,"homepage":"https://npm.im/teaful","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/teafuljs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-03-11T20:18:22.000Z","updated_at":"2026-03-22T10:10:40.000Z","dependencies_parsed_at":"2022-07-18T03:00:32.781Z","dependency_job_id":null,"html_url":"https://github.com/teafuljs/teaful","commit_stats":null,"previous_names":["aralroca/fragstore"],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/teafuljs/teaful","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teafuljs%2Fteaful","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teafuljs%2Fteaful/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teafuljs%2Fteaful/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teafuljs%2Fteaful/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/teafuljs","download_url":"https://codeload.github.com/teafuljs/teaful/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teafuljs%2Fteaful/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31292789,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T21:15:39.731Z","status":"ssl_error","status_checked_at":"2026-04-01T21:15:34.046Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["easy","fragmented","javascript","management","performance","preact","react","state","state-management","store","teaful","tiny"],"created_at":"2024-07-30T23:00:44.032Z","updated_at":"2026-04-01T23:03:10.300Z","avatar_url":"https://github.com/teafuljs.png","language":"TypeScript","readme":" \u003cimg src=\"/assets/logo.svg\" width=\"200\" alt=\"Teaful\" align=\"right\" /\u003e\n\n\u003ch1\u003e\n\u003cdiv\u003e\u003cb\u003eTeaful\u003c/b\u003e\u003c/div\u003e\n\u003c/h1\u003e\n\n_Tiny, easy and powerful **React state management** library_\n\n[![npm version](https://badge.fury.io/js/teaful.svg)](https://badge.fury.io/js/teaful)\n[![gzip size](https://img.badgesize.io/https://unpkg.com/teaful?compression=gzip\u0026label=gzip)](https://unpkg.com/teaful)\n[![CI Status](https://github.com/teafuljs/teaful/actions/workflows/test.yml/badge.svg)](https://github.com/teafuljs/teaful/actions/workflows/test.yml)\n[![Maintenance Status](https://badgen.net/badge/maintenance/active/green)](https://github.com/teafuljs/teaful#maintenance-status)\n[![Weekly downloads](https://badgen.net/npm/dw/teaful?color=blue)](https://www.npmjs.com/package/teaful)\n[![GitHub Discussions: Chat With Us](https://badgen.net/badge/discussions/chat%20with%20us/purple)](https://github.com/teafuljs/teaful/discussions)\n[![PRs Welcome][badge-prwelcome]][prwelcome]\u003c!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section --\u003e\n[![All Contributors](https://img.shields.io/badge/all_contributors-11-orange.svg?style=flat-square)](#contributors-)\n\u003c!-- ALL-CONTRIBUTORS-BADGE:END --\u003e\n\n[badge-prwelcome]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\n[prwelcome]: http://makeapullrequest.com\n\n\n## What advantages does it have? ✨\n\n\u003cul\u003e\n    \u003cli\u003e📦  ・\u003cb\u003eTiny\u003c/b\u003e: Less than 1kb package to manage your state in React and Preact.\u003c/li\u003e\n    \u003cli\u003e🌱  ・\u003cb\u003eEasy\u003c/b\u003e: You don't need actions, reducers, selectors, connect, providers, etc. Everything can be done in the simplest and most comfortable way.\u003c/li\u003e\n    \u003cli\u003e🚀  ・\u003cb\u003ePowerful\u003c/b\u003e: When a store property is updated, only its components are re-rendered. It's not re-rendering components that use other store properties.\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cdiv align=\"center\" \u003e\n\u003cimg width=\"800\" src=\"/assets/teaful.png\" /\u003e\n\u003c/div\u003e\n\n\n## Guide 🗺\n\n- [1. Installation 🧑🏻‍💻](#installation-)\n- [2. Init your store 👩🏽‍🎨](#init-your-store-)\n  - [createStore](#createstore)\n  - [How to export](#how-to-export)\n- [3. Manage the store 🕹](#manage-the-store-)\n  - [useStore hook](#usestore-hook)\n  - [setStore helper](#setstore-helper)\n  - [getStore helper](#getstore-helper)\n  - [withStore HoC](#withstore-hoc)\n- [4. Register events after an update 🚦](#register-events-after-an-update-)\n- [5. How to... 🧑‍🎓](#how-to-)\n  - [Add a new store property](#add-a-new-store-property)\n  - [Use more than one store](#use-more-than-one-store)\n  - [Update several portions avoiding rerenders in the rest](#update-several-portions-avoiding-rerenders-in-the-rest)\n  - [Define calculated properties](#define-calculated-properties)\n- [6. Teaful Devtools 🛠](#teaful-devtools-)\n- [7. Addons and extras 🌀](#addons-and-extras-)\n- [8. Examples 🖥](#examples-)\n- [9. Roadmap 🛣](#roadmap-)\n- [10. Contributors ✨](#contributors-)\n\n## Installation 🧑🏻‍💻\n\n```sh\nyarn add teaful\n# or\nnpm install teaful --save\n```\n\n## Init your store 👩🏽‍🎨\n\nEach 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.\n\n### createStore\n\n```js\nimport createStore from \"teaful\";\n\nconst { useStore } = createStore();\n```\n\nOr also with an initial store:\n\n```js\nconst initialStore = {\n  cart: { price: 0, items: [] },\n};\nconst { useStore, getStore } = createStore(initialStore);\n```\n\nOr also with an event that is executed after every update:\n\n```js\nconst initialStore = {\n  cart: { price: 0, items: [] },\n};\n\nfunction onAfterUpdate({ store, prevStore }) {\n  console.log(\"This callback is executed after an update\");\n}\n\nconst { useStore } = createStore(initialStore, onAfterUpdate);\n```\n\n_Input:_\n\n| name            | type          | required | description                                                                                              |\n| --------------- | ------------- | -------- | -------------------------------------------------------------------------------------------------------- |\n| `initialStore`  | `object\u003cany\u003e` | `false`  | Object with your initial store.                                                                          |\n| `onAfterUpdate` | `function`    | `false`  | Function that is executed after each property change. More [details](#register-events-after-an-update-). |\n\n_Output:_\n\n| name        | type    | description                                                                                                                                                                                             | example                                           |\n| ----------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |\n| `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](#usestore-hook).                 | `const [price, setPrice] = useStore.cart.price()` |\n| `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](#getstore-helper). | `const [price, setPrice] = getStore.cart.price()` |\n| `setStore`  | `Proxy` | It's a proxy helper to modify a store property outside (or inside) components. More [info](#setstore-helper). | `setStore.user.name('Aral')` or `setStore.cart.price(price =\u003e price + 10)`  |\n| `withStore` | `Proxy` | HoC with `useStore` inside. Useful for components that are not functional. More [info](#withstore-hoc).                                                                                                 | `withStore.cart.price(MyComponent)`               |\n\n### How to export\n\nWe recommend using this type of export:\n\n```js\n// ✅\nexport const { useStore, getStore, withStore } = createStore({\n  cart: { price: 0, items: [] },\n});\n```\n\nThis way you can import it with:\n\n```js\n// ✅\nimport { useStore } from '../store'\n```\n\nAvoid using a default export with all:\n\n```js\n// ❌\nexport default createStore({ cart: { price: 0, items: [] } });\n```\n\nBecause then you won't be able to do this:\n\n```js\n// ❌  It's not working well with proxies\nimport { useStore } from '../store'\n```\n\n## Manage the store 🕹\n\n### useStore hook\n\nIt'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.\n\n```js\nimport createStore from \"teaful\";\n\nconst { useStore } = createStore({\n  username: \"Aral\",\n  count: 0,\n  age: 31,\n  cart: {\n    price: 0,\n    items: [],\n  },\n});\n\nfunction Example() {\n  const [username, setUsername] = useStore.username();\n  const [cartPrice, setCartPrice] = useStore.cart.price();\n\n  return (\n    \u003c\u003e\n      \u003cbutton onClick={() =\u003e setUsername(\"AnotherUserName\")}\u003e\n        Update {username}\n      \u003c/button\u003e\n      \u003cbutton onClick={() =\u003e setCartPrice((v) =\u003e v + 1)}\u003e\n        Increment price: {cartPrice}€\n      \u003c/button\u003e\n    \u003c/\u003e\n  );\n}\n```\n\nHowever, it's also possible to use the `useStore` hook to use **all the store**.\n\n```js\nfunction Example() {\n  const [store, setStore] = useStore();\n\n  return (\n    \u003c\u003e\n      \u003cbutton\n        onClick={() =\u003e\n          setStore((s) =\u003e ({\n            ...s,\n            username: \"AnotherUserName\",\n          }))\n        }\n      \u003e\n        Update {store.username}\n      \u003c/button\u003e\n      \u003cbutton\n        onClick={() =\u003e\n          setStore((s) =\u003e ({\n            ...s,\n            cart: { ...s.cart, price: s.cart.price + 1 },\n          }))\n        }\n      \u003e\n        Increment price: {store.cart.price}€\n      \u003c/button\u003e\n    \u003c/\u003e\n  );\n}\n```\n\n_Input:_\n\n| name                  | type       | description                                                                                                                                                                                                                                    | example                                                                                                                                                                                                    |\n| --------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| 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)`                                                                                                                                                         |\n| 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)`\u003cdiv\u003e\u003csmall\u003eAnd the function:\u003c/small\u003e\u003c/div\u003e\u003cdiv\u003e`function onAfterUpdate({ store, prevStore }){ console.log({ store, prevStore }) }`\u003c/div\u003e |\n\n_Output:_\n\nIs an `Array` with **2** items:\n\n| name         | type       | description                                                                             | example                                                                                                                                                                                                                                                                                                                                                                 |\n| ------------ | ---------- | --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| value        | `any`      | The value of the store portion indicated with the proxy.                                | A store portion \u003cdiv\u003e`const [price] = useStore.cart.price()`\u003c/div\u003eAll store: \u003cdiv\u003e `const [store] = useStore()`\u003c/div\u003e                                                                                                                                                                                                                                                   |\n| update value | `function` | Function to update the store property indicated with the proxy.                         | Updating a store portion:\u003cdiv\u003e`const [count, setCount] = useStore.count(0)`\u003c/div\u003eWay 1:\u003cdiv\u003e`setCount(count + 1)`\u003c/div\u003eWay 1:\u003cdiv\u003e`setCount(c =\u003e c + 1)`\u003c/div\u003e\u003cdiv\u003e-------\u003c/div\u003eUpdating all store:\u003cdiv\u003e`const [store, updateStore] = useStore()`\u003c/div\u003eWay 1:\u003cdiv\u003e`updateStore({ ...store, count: 2 }))`\u003c/div\u003eWay 1:\u003cdiv\u003e`updateStore(s =\u003e ({ ...s, count: 2 }))`\u003c/div\u003e |\n\n\n### setStore helper\n\nUseful helper to modify the store from anywhere (outside/inside components).\n\nExample:\n\n```js\nconst initialStore = { count: 0, name: 'Aral' }\nconst { setStore } = createStore(initialStore);\n\nconst resetStore = () =\u003e setStore(initialStore);\nconst resetCount = () =\u003e setStore.count(initialStore.count);\nconst resetName = () =\u003e setStore.name(initialStore.name);\n\n// Component without any re-render (without useStore hook)\nfunction Resets() {\n  return (\n    \u003c\u003e\n      \u003cbutton onClick={resetStore}\u003e\n        Reset store\n      \u003c/button\u003e\n      \u003cbutton onClick={resetCount}\u003e\n        Reset count\n      \u003c/button\u003e\n      \u003cbutton onClick={resetName}\u003e\n        Reset name\n      \u003c/button\u003e\n    \u003c/\u003e\n  );\n}\n```\n\nAnother example:\n\n```js\nconst { useStore, setStore } = createStore({\n  firstName: '',\n  lastName: '' \n});\n\nfunction ExampleOfForm() {\n  const [formFields] = useStore()\n\n  return Object.entries(formFields).map(([key, value]) =\u003e (\n    \u003cinput \n      defaultValue={value} \n      type=\"text\"\n      key={key}\n      onChange={e =\u003e {\n        // Update depending the key attribute\n        setStore[key](e.target.value)\n      }} \n    /\u003e\n  ))\n}\n```\n\nThis second example only causes re-renders in the components that consume the property that has been modified. \n\nIn this way:\n\n```js\nconst [formFields, setFormFields] = useStore()\n// ...\nsetFormFields(s =\u003e ({ ...s, [key]: e.target.value })) // ❌\n```\n\nThis 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.\n\n### getStore helper\n\nIt works exactly like `useStore` but with **some differences**:\n\n- It **does not make a subscription**. So it is no longer a hook and you can use it as a helper wherever you want.\n- It's **not possible to register events** that are executed after a change.\n\n  ```js\n  getStore.cart.price(0, onAfterPriceChange); // ❌\n\n  function onAfterPriceChange({ store, prevStore }) {\n    // ...\n  }\n  ```\n\n  - If the intention is to register events that last forever, it has to be done within the `createStore`:\n\n  ```js\n  const { getStore } = createStore(initialStore, onAfterUpdate); // ✅\n\n  function onAfterUpdate({ store, prevStore }) {\n    // ..\n  }\n  ```\n\nVery useful to use it:\n\n- **Outside components**: helpers, services, etc.\n- **Inside components**: Avoiding rerenders if you want to consume it inside events, when you only use the updater `const [, setCount] = getStore.count()`, etc.\n\nExample:\n\n```js\nimport { useState } from \"react\";\n\nconst initialStore = { count: 0 }\nconst { getStore } = createStore(initialStore);\n\nfunction Example1() {\n  return (\n    \u003cbutton onClick={() =\u003e {\n      const [, setStore] = getStore();\n      setStore(initialStore)\n    }}\u003e\n      Reset store\n    \u003c/button\u003e\n  );\n}\n\nfunction Example2() {\n  const [newCount, setNewCount] = useState();\n\n  function saveIncreasedCount(e) {\n    e.preventDefault();\n    const [count, setCount] = getStore.count();\n    if (newCount \u003e count) setCount(newCount);\n    else alert(\"You should increase the value\");\n  }\n\n  return (\n    \u003cform onSubmit={saveIncreasedCount}\u003e\n      \u003cinput\n        value={newCount}\n        onChange={(e) =\u003e setNewCount(e.target.valueAsNumber)}\n        type=\"number\"\n      /\u003e\n      \u003cbutton\u003eSave the increased count value\u003c/button\u003e\n    \u003c/form\u003e\n  );\n}\n```\n\n### withStore HoC\n\nIt's a wrapper of the `useStore` for non-functional components. Where you receive the same thing that the `useStore` hook returns inside `this.props.store`.\n\nExample with a store portion:\n\n```js\nconst { withStore } = createStore();\n\nclass Counter extends Component {\n  render() {\n    const [count, setCount] = this.props.store;\n    return (\n      \u003cdiv\u003e\n        \u003ch1\u003e{count}\u003c/h1\u003e\n        \u003cbutton onClick={() =\u003e setCount((v) =\u003e v + 1)}\u003e+\u003c/button\u003e\n        \u003cbutton onClick={() =\u003e setCount((v) =\u003e v - 1)}\u003e-\u003c/button\u003e\n        \u003cbutton onClick={() =\u003e setCount(0)}\u003ereset\u003c/button\u003e\n      \u003c/div\u003e\n    );\n  }\n}\n\n// Similar to useStore.counter.count(0)\nconst CounterWithStore = withStore.counter.count(Counter, 0);\n```\n\nExample with all store:\n\n```js\nconst { withStore } = createStore({ count: 0 });\n\nclass Counter extends Component {\n  render() {\n    const [store, setStore] = this.props.store;\n    return (\n      \u003cdiv\u003e\n        \u003ch1\u003e{store.count}\u003c/h1\u003e\n        \u003cbutton onClick={() =\u003e setStore({ count: store.count + 1 })}\u003e+\u003c/button\u003e\n        \u003cbutton onClick={() =\u003e setStore({ count: store.count - 1 })}\u003e-\u003c/button\u003e\n        \u003cbutton onClick={() =\u003e setStore({ count: 0 })}\u003ereset\u003c/button\u003e\n      \u003c/div\u003e\n    );\n  }\n}\n\n// Similar to useStore()\nconst CounterWithStore = withStore(Counter);\n```\n\nThe **only difference** with the `useStore` is that instead of having 2 parameters (initialValue, onAfterUpdate), it has 3 where the **first one is mandatory** and the other 2 are not (**Component**, **initialValue**, **onAfterUpdate**).\n\n## Register events after an update 🚦\n\nIt is possible to register an event after each update. This can be useful for validating properties, storing error messages, optimistic updates...\n\nThere are 2 ways to register:\n\n- **Permanent** events: Inside `createStore`. This event will always be executed for each change made within the store.\n\n  ```js\n  export const { useStore, getStore } = createStore(\n    initialStore,\n    onAfterUpdate\n  );\n\n  function onAfterUpdate({ store, prevStore }) {\n    // Add an error msg\n    if (store.count \u003e 99 \u0026\u0026 !store.errorMsg) {\n      const [, setErrorMsg] = getStore.errorMsg();\n      setErrorMsg(\"The count value should be lower than 100\");\n      return;\n    }\n    // Remove error msg\n    if (store.count \u003c= 99 \u0026\u0026 store.errorMsg) {\n      const [, setErrorMsg] = getStore.errorMsg();\n      setErrorMsg();\n    }\n  }\n  ```\n\n- **Temporal** events: Inside `useStore` / `withStore`. These events will be executed for each change in the store (or indicated portion) **only during the life of the component**, when the component is unmounted the event is removed.\n\n  ```js\n  function Count() {\n    const [count, setCount] = useStore.count(0, onAfterUpdate);\n    const [errorMsg, setErrorMsg] = useStore.errorMsg();\n\n    // The event lasts as long as this component lives\n    function onAfterUpdate({ store, prevStore }) {\n      // Add an error msg\n      if (store.count \u003e 99 \u0026\u0026 !store.errorMsg) {\n        setErrorMsg(\"The count value should be lower than 100\");\n        return;\n      }\n      // Remove error msg\n      if (store.count \u003e= 99 \u0026\u0026 store.errorMsg) {\n        setErrorMsg();\n      }\n    }\n\n    return (\n      \u003c\u003e\n        {errorMsg \u0026\u0026 \u003cdiv className=\"erorMsg\"\u003e{errorMsg}\u003c/div\u003e}\n        \u003cdiv className=\"count\"\u003e{count}\u003c/div\u003e\n        \u003cbutton onClick={() =\u003e setCount((v) =\u003e v + 1)}\u003eIncrement\u003c/button\u003e\n      \u003c/\u003e\n    );\n  }\n  ```\n\n## How to... 🧑‍🎓\n\n### Add a new store property\n\nYou can use `useStore` / `getStore` / `withStore` even if the property does not exist inside the store, and create it on the fly.\n\n```js\nconst { useStore } = createStore({ username: \"Aral\" });\n\nfunction CreateProperty() {\n  const [price, setPrice] = useStore.cart.price(0); // 0 as initial value\n\n  return \u003cdiv\u003ePrice: {price}\u003c/div\u003e;\n}\n\nfunction OtherComponent() {\n  // store now is { username: 'Aral', cart: { price: 0 } }\n  const [store] = useStore();\n  console.log(store.cart.price); // 0\n  // ...\n}\n```\n\nIt's **not mandatory to indicate the initial value**, you can create the property in a following step with the updater.\n\n```js\nconst { useStore } = createStore({ username: \"Aral\" });\n\nfunction CreateProperty() {\n  const [cart, setCart] = useStore.cart();\n\n  useEffect(() =\u003e {\n    initCart();\n  }, []);\n  async function initCart() {\n    const newCart = await fetch(\"/api/cart\");\n    setCart(newCart);\n  }\n\n  if (!cart) return null;\n\n  return \u003cdiv\u003ePrice: {cart.price}\u003c/div\u003e;\n}\n```\n\n### Use more than one store\n\nYou can have as many stores as you want. The only thing you have to do is to use as many `createStore` as stores you want.\n\nstore.js\n\n```js\nimport createStore from \"teaful\";\n\nexport const { useStore: useCart } = createStore({ price: 0, items: [] });\nexport const { useStore: useCounter } = createStore({ count: 0 });\n```\n\nCart.js\n\n```js\nimport { useCart } from \"./store\";\n\nexport default function Cart() {\n  const [price, setPrice] = useCart.price();\n  // ... rest\n}\n```\n\nCounter.js\n\n```js\nimport { useCounter } from \"./store\";\n\nexport default function Counter() {\n  const [count, setCount] = useCounter.count();\n  // ... rest\n}\n```\n\n### Update several portions avoiding rerenders in the rest\n\nIf you do this it causes a rerender to all the properties of the store:\n\n```js\n// 😡\nconst [store, setStore] = useStore();\nsetStore({ ...store, count: 10, username: \"\" });\n```\n\nAnd if you do the next, you convert the whole store into only 2 properties (`{ count: 10, username: '' }`), and you will remove the rest:\n\n```js\n// 🥵\nconst [store, setStore] = useStore();\nsetStore({ count: 10, username: \"\" });\n```\n\nIf you have to update several properties and you don't want to disturb the rest of the components that are using other store properties you can create a helper with `getStore`.\n\n```js\nexport const { useStore, setStore } = createStore(initialStore);\n\nexport function setFragmentedStore(fields) {\n  Object.entries(fields).forEach(([key, value]) =\u003e {\n    setStore[key](value);\n  });\n}\n```\n\nAnd use it wherever you want:\n\n```js\n// 🤩\nimport { setFragmentedStore } from \"./store\";\n\n// ...\nsetFragmentedStore({ count: 10, username: \"\" });\n```\n\n### Define calculated properties\n\nIt's possible to use the `setStore` together with the function that is executed after each update to have store properties calculated from others.\n\nIn this example the cart price value will always be a value calculated according to the array of items:\n\n```js\nexport const { useStore, setStore } = createStore(\n  {\n    cart: {\n      price: 0,\n      items: [],\n    },\n  },\n  onAfterUpdate\n);\n\nfunction onAfterUpdate({ store }) {\n  const { items, price } = store.cart;\n  const calculatedPrice = items.length * 3;\n\n  // Price always will be items.length * 3\n  if (price !== calculatedPrice) {\n    setStore.cart.price(calculatedPrice);\n  }\n}\n```\n\nIt's an anti-pattern? Not in Teaful 😊. As only the fragments of the store are updated and not the whole store, it is the same as updating both properties (`cart.items` and `cart.price`) instead of just `cart.items`.  The anti-pattern comes when it causes unnecessary rerenders, but this is not the case. Only the components that use `cart.items` and `cart.price` are rerendered and not the others.\n\n\n## Teaful Devtools 🛠\n\nTo debug your stores, you can use [Teaful DevTools](https://github.com/teafuljs/teaful-devtools).\n\n\u003cimg alt=\"Teaful DevTools\" src=\"https://github.com/teafuljs/teaful-devtools/blob/master/demo.png?raw=true\" /\u003e\n\n\n## Addons and extras 🌀\n\nTo facilitate the creation of libraries that extend Teaful (such as [`teaful-devtools`](https://github.com/teafuljs/teaful-devtools)), we allow the possibility to add an extra that:\n\n- Have access to everything returned by each `createStore` consumed: `getStore`, `useStore`, `withStore`.\n- Return an object with new elements to be returned by each `createStore`. It's optional, if nothing is returned it will continue to return the usual. If, for example, you return `{ getCustomThing }` will do an assign with what is currently returned by the `createStore`.\n- Ability to **subscribe**, **unsubscribe** and **notify** in each `createStore`.\n\nFor that, use the `createStore.ext` function.\n\n\u003csmall\u003eteaful-yourlib:\u003c/small\u003e\n\n```js\nimport createStore from 'teaful'\n\ncreateStore.ext(({ getStore,  }, subscription) =\u003e {\n    // s = subscribe (minified by Teaful)\n    //     \".\" -\u003e all store\n    //     \".cart\" -\u003e only inside cart\n    //     \".cart.price\" -\u003e only inside cart.price\n    // n = notify (minified by Teaful)\n    // u = unsubscribe (minified by Teaful)\n    subscription.s(\".\", ({ store, prevStore }) =\u003e {\n      // This will be executed in any store (\".\") change.\n    });\n\n    // optional\n    return { getCustomThing: () =\u003e console.log('example') }\n})\n```\n\nThen, your library should be imported at the top:\n\n```js\nimport 'teaful-yourlib'\nimport { render } from 'preact';\nimport App from './components/App';\n\nrender(\u003cApp /\u003e, document.getElementById('root'));\n```\n\n## Examples 🖥\n\nWe will expand the examples over time. For now you can use this Codesandbox:\n\n- [Example of store - CodeSandbox](https://codesandbox.io/s/teaful-example-4p5dv?file=/src/store.js)\n- [Counter with ESM](https://github.com/teafuljs/teaful/tree/master/examples/counter-with-esm)\n- [Todo list](https://github.com/teafuljs/teaful/tree/master/examples/todo-list)\n- [Example with an API list](https://github.com/teafuljs/teaful/blob/master/examples/api-list/README.md)\n\n## Roadmap 🛣\n\n**For 1.0**:\n\n- [x] React support\n- [x] Teaful DevTools\n- [x] TypeScript types support\n- [x] Migrate full Teaful project to TypeScript\n- [ ] React Native support\n- [ ] Vanilla JavaScript support\n- [ ] Create a documentation website\n- [ ] Add more examples: with Next.js, Remix, Preact, React Native...\n\n\n**Optional for 1.0 (else +1.0):**\n\n- [ ] Svelte support\n- [ ] Vue support\n- [ ] Solid support\n\n_If you think that there is something that should be preindicated by version 1.0 please report it as an issue or discussion 🙏_\n\n## Contributors ✨\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\"\u003e\u003ca href=\"https://aralroca.com\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/13313058?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eAral Roca Gomez\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#maintenance-aralroca\" title=\"Maintenance\"\u003e🚧\u003c/a\u003e \u003ca href=\"https://github.com/teafuljs/teaful/commits?author=aralroca\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\"\u003e\u003ca href=\"https://twitter.com/danielofair\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/4655428?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eDanielo Artola\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#infra-danielart\" title=\"Infrastructure (Hosting, Build-Tools, etc)\"\u003e🚇\u003c/a\u003e \u003ca href=\"https://github.com/teafuljs/teaful/commits?author=danielart\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\"\u003e\u003ca href=\"https://shinshin86.com\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/8216064?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eYuki Shindo\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#infra-shinshin86\" title=\"Infrastructure (Hosting, Build-Tools, etc)\"\u003e🚇\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/dididy\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/16266103?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eYONGJAE LEE(이용재)\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/teafuljs/teaful/issues?q=author%3Adididy\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\"\u003e\u003ca href=\"https://juejin.cn/user/4318537404123688/posts\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/16329407?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eniexq\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/teafuljs/teaful/commits?author=niexq\" title=\"Documentation\"\u003e📖\u003c/a\u003e \u003ca href=\"#infra-niexq\" title=\"Infrastructure (Hosting, Build-Tools, etc)\"\u003e🚇\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\"\u003e\u003ca href=\"https://nekonako.github.io\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/46141275?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003enekonako\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/teafuljs/teaful/commits?author=nekonako\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\"\u003e\u003ca href=\"https://shubhamverma.dev/\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/29898106?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eShubham\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/teafuljs/teaful/commits?author=shubhamV123\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\"\u003e\u003ca href=\"http://siddharthborderwala.com\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/54456279?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eSiddharth Borderwala\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/teafuljs/teaful/commits?author=siddharthborderwala\" title=\"Documentation\"\u003e📖\u003c/a\u003e \u003ca href=\"#infra-siddharthborderwala\" title=\"Infrastructure (Hosting, Build-Tools, etc)\"\u003e🚇\u003c/a\u003e \u003ca href=\"https://github.com/teafuljs/teaful/commits?author=siddharthborderwala\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/wh5938316\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/16160933?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ewatcher\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/teafuljs/teaful/commits?author=wh5938316\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\"\u003e\u003ca href=\"http://www.schommers.be\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/6319092?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ePhilippe Schommers\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/teafuljs/teaful/commits?author=filoozom\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/ktdd\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/13239117?v=4?s=100\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ektdd\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/teafuljs/teaful/commits?author=ktdd\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-restore --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n","funding_links":[],"categories":["Uncategorized","Code Design","TypeScript","react","State Managers","List"],"sub_categories":["Uncategorized","Data Store"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteafuljs%2Fteaful","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fteafuljs%2Fteaful","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteafuljs%2Fteaful/lists"}