Teaful - Tiny, easy and powerful React state management

Overview

Teaful

Teaful

Tiny, easy and powerful React state management library

npm version gzip size CI Status Maintenance Status Weekly downloads GitHub Discussions: Chat With Us PRs Welcome All Contributors

What advantages does it have? ✨

  • 📦 ・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.

Guide 🗺

Installation 🧑🏻‍💻

yarn add teaful
# or
npm install teaful --save

Init your store 👩🏽‍🎨

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.

createStore

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()
withStore Proxy HoC with useStore inside. Useful for components that are not functional. More info. withStore.cart.price(MyComponent)

How to export

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'

Manage the store 🕹

useStore hook

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 (
    <>
      <button onClick={() => setUsername("AnotherUserName")}>
        Update {username}
      </button>
      <button onClick={() => setCartPrice((v) => v + 1)}>
        Increment price: {cartPrice}€
      </button>
    </>
  );
}

However, it's also possible to use the useStore hook to use all the store.

Update {store.username} ); } ">
function Example() {
  const [store, setStore] = useStore();

  return (
    <>
      <button
        onClick={() =>
          setStore((s) => ({
            ...s,
            username: "AnotherUserName",
          }))
        }
      >
        Update {store.username}
      </button>
      <button
        onClick={() =>
          setStore((s) => ({
            ...s,
            cart: { ...s.cart, price: s.cart.price + 1 },
          }))
        }
      >
        Increment price: {store.cart.price}€
      </button>
    </>
  );
}

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)
And the function:
function onAfterUpdate({ store, prevStore }){ console.log({ store, prevStore }) }

Output:

Is an Array with 3 items:

name type description example
value any The value of the store portion indicated with the proxy. A store portion
const [price] = useStore.cart.price()
All store:
const [store] = useStore()
update value function Function to update the store property indicated with the proxy. Updating a store portion:
const [count, setCount] = useStore.count(0)
Way 1:
setCount(count + 1)
Way 1:
setCount(c => c + 1)
-------
Updating all store:
const [store, updateStore] = useStore()
Way 1:
updateStore({ ...store, count: 2 }))
Way 1:
updateStore(s => ({ ...s, count: 2 }))
reset value function Function that reset the store property indicated with the proxy to their initial value. Reset store portion:
const [,,resetCount] = useStore.count()
resetCount()
Put counter to 0 again (initial value defined inside the createStore).
-------
Reset all store:
const [,,resetStore] = useStore()
resetStore()
All store portions to their initial values.

getStore helper

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 }) {
      // ...
    }
    • If the intention is to register events that last forever, it has to be done within the createStore:
    const { getStore } = createStore(initialStore, onAfterUpdate); // ✅
    
    function onAfterUpdate({ store, prevStore }) {
      // ..
    }

Very useful to use it:

  • Outside components: helpers, services, etc.
  • Inside components: Avoiding rerenders if you want to consume it inside events, when you only use the updaters const [, setCount, resetCount] = getStore.count(), etc.

Example:

Reset store; } function Example2() { const [newCount, setNewCount] = useState(); function saveIncreasedCount(e) { e.preventDefault(); const [count, setCount] = getStore.count(); if (newCount > count) setCount(newCount); else alert("You should increase the value"); } return (
setNewCount(e.target.valueAsNumber)} type="number" />
); } ">
import { useState } from "react";

const { getStore } = createStore({ count: 0 });

function Example1() {
  const resetStore = getStore()[2];
  return <button onClick={resetStore}>Reset store</button>;
}

function Example2() {
  const [newCount, setNewCount] = useState();

  function saveIncreasedCount(e) {
    e.preventDefault();
    const [count, setCount] = getStore.count();
    if (newCount > count) setCount(newCount);
    else alert("You should increase the value");
  }

  return (
    <form onSubmit={saveIncreasedCount}>
      <input
        value={newCount}
        onChange={(e) => setNewCount(e.target.valueAsNumber)}
        type="number"
      />
      <button>Save the increased count value</button>
    </form>
  );
}

withStore HoC

It'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.

Example with a store portion:

const { withStore } = createStore();

class Counter extends Component {
  render() {
    const [count, setCount, resetCount] = this.props.store;
    return (
      <div>
        <h1>{count}</h1>
        <button onClick={() => setCount((v) => v + 1)}>+</button>
        <button onClick={() => setCount((v) => v - 1)}>-</button>
        <button onClick={resetCount}>reset</button>
      </div>
    );
  }
}

// Similar to useStore.counter.count(0)
const CounterWithStore = withStore.counter.count(Counter, 0);

Example with all store:

const { withStore } = createStore({ count: 0 });

class Counter extends Component {
  render() {
    const [store, setStore, resetStore] = this.props.store;
    return (
      <div>
        <h1>{store.count}</h1>
        <button onClick={() => setStore({ count: store.count + 1 })}>+</button>
        <button onClick={() => setStore({ count: store.count - 1 })}>-</button>
        <button onClick={resetStore}>reset</button>
      </div>
    );
  }
}

// Similar to useStore()
const CounterWithStore = withStore(Counter);

The 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).

Register events after an update 🚦

It is possible to register an event after each update. This can be useful for validating properties, storing error messages, optimistic updates...

There are 2 ways to register:

  • Permanent events: Inside createStore. This event will always be executed for each change made within the store.

    export const { useStore, getStore } = createStore(
      initialStore,
      onAfterUpdate
    );
    
    function onAfterUpdate({ store, prevStore }) {
      // Add an error msg
      if (store.count > 99 && !store.errorMsg) {
        const [, setErrorMsg] = getStore.errorMsg();
        setErrorMsg("The count value should be lower than 100");
        return;
      }
      // Remove error msg
      if (store.count <= 99 && store.errorMsg) {
        const [, setErrorMsg] = getStore.errorMsg();
        setErrorMsg();
      }
    }
  • 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.

    = 99 && store.errorMsg) { setErrorMsg(); } } return ( <> {errorMsg &&
    {errorMsg}
    }
    {count}
    ); } ">
    function Count() {
      const [count, setCount] = useStore.count(0, onAfterUpdate);
      const [errorMsg, setErrorMsg] = useStore.errorMsg();
    
      // The event lasts as long as this component lives
      function onAfterUpdate({ store, prevStore }) {
        // Add an error msg
        if (store.count > 99 && !store.errorMsg) {
          setErrorMsg("The count value should be lower than 100");
          return;
        }
        // Remove error msg
        if (store.count >= 99 && store.errorMsg) {
          setErrorMsg();
        }
      }
    
      return (
        <>
          {errorMsg && <div className="erorMsg">{errorMsg}</div>}
          <div className="count">{count}</div>
          <button onClick={() => setCount((v) => v + 1)}>Increment</button>
        </>
      );
    }

How to... 🧑‍🎓

Add a new store property

You can use useStore / getStore / withStore even if the property does not exist inside the store, and create it on the fly.

Price: {price}
; } function OtherComponent() { // store now is { username: 'Aral', cart: { price: 0 } } const [store] = useStore(); console.log(store.cart.price); // 0 // ... } ">
const { useStore } = createStore({ username: "Aral" });

function CreateProperty() {
  const [price, setPrice] = useStore.cart.price(0); // 0 as initial value

  return <div>Price: {price}</div>;
}

function OtherComponent() {
  // store now is { username: 'Aral', cart: { price: 0 } }
  const [store] = useStore();
  console.log(store.cart.price); // 0
  // ...
}

It's not mandatory to indicate the initial value, you can create the property in a following step with the updater.

{ initCart(); }, []); async function initCart() { const newCart = await fetch("/api/cart"); setCart(newCart); } if (!cart) return null; return
Price: {cart.price}
; } ">
const { useStore } = createStore({ username: "Aral" });

function CreateProperty() {
  const [cart, setCart] = useStore.cart();

  useEffect(() => {
    initCart();
  }, []);
  async function initCart() {
    const newCart = await fetch("/api/cart");
    setCart(newCart);
  }

  if (!cart) return null;

  return <div>Price: {cart.price}</div>;
}

Reset a store property

You can use the 3th array item from useStore / getStore / withStore. It's a function to return the value to its initial value.

const [item, setItem, resetItem] = useStore.item();
// ...
resetItem();

If you only want the reset function and not the value, we recommend using the getStore to avoid creating a subscription and avoid unnecessary rerenders.

const [, , resetItem] = getStore.item();
// or...
const resetItem = getStore.item()[2];

Reset all the store

The same thing works to reset the entire store to its initial value.

const [store, setStore, resetStore] = useStore();
// ...
resetStore();

Use more than one store

You 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.

store.js

import createStore from "teaful";

export const { useStore: useCart } = createStore({ price: 0, items: [] });
export const { useStore: useCounter } = createStore({ count: 0 });

Cart.js

import { useCart } from "./store";

export default function Cart() {
  const [price, setPrice] = useCart.price();
  // ... rest
}

Counter.js

import { useCounter } from "./store";

export default function Counter() {
  const [count, setCount] = useCounter.count();
  // ... rest
}

Update several portions avoiding rerenders in the rest

If you do this it causes a rerender to all the properties of the store:

// 😡
const [store, setStore] = useStore();
setStore({ ...store, count: 10, username: "" });

And if you do the next, you convert the whole store into only 2 properties ({ count: 10, username: '' }), and you will remove the rest:

// 🥵
const [store, setStore] = useStore();
setStore({ count: 10, username: "" });

If 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.

export const { useStore, getStore } = createStore(initialStore);

export function setStore(fields) {
  Object.keys(fields).forEach((key) => {
    const setStoreField = getStore[key]()[1];
    setStoreField(fields[key]);
  });
}

And use it wherever you want:

// 🤩
import { setStore } from "./store";

// ...
setStore({ count: 10, username: "" });

Define calculated properties

It's possible to use the getStore together with the function that is executed after each update to have store properties calculated from others.

In this example the cart price value will always be a value calculated according to the array of items:

export const { useStore, getStore } = createStore(
  {
    cart: {
      price: 0,
      items: [],
    },
  },
  onAfterUpdate
);

function onAfterUpdate({ store }) {
  const { items, price } = store.cart;
  const calculatedPrice = items.length * 3;

  // Price always will be items.length * 3
  if (price !== calculatedPrice) {
    const [, setPrice] = getStore.cart.price();
    setPrice(calculatedPrice);
  }
}

Examples 🖥

We will expand the examples over time. For now you can use this Codesandbox:

Roadmap 🛣

  • React support
  • TypeScript support
  • Vanilla JavaScript support
  • Svelte support
  • Vue support

Contributors ✨

Thanks goes to these wonderful people (emoji key):


Aral Roca Gomez

🚧 💻

Danielo Artola

🚇

Yuki Shindo

🚇

YONGJAE LEE(이용재)

🐛

niexq

📖 🚇

This project follows the all-contributors specification. Contributions of any kind welcome!

Comments
  • New documentation site

    New documentation site

    I have setup a documentation site for this library. I am using nextjs and nextra starter for creating a static documentation site.

    The docs directory contains the next-js app, it uses yarn, keeping the package manager uniform.

    Currently, I have only created 2 pages of documentation, you can have a look at it - https://teaful-docs.vercel.app/.

    Let me know what you think @aralroca

    opened by siddharthborderwala 33
  • Array with one less item breaks the store.

    Array with one less item breaks the store.

    What version of this package are you using? 0.5.0

    What operating system, Node.js, and npm version? 14

    What happened? My Store has an array export const { Store, useStore } = createStore({ upfiles: [], projectNumber: 'None' });

    using: const [upfiles, setUpfiles] = useStore.upfiles(); ina any component, I can add items to the upfiles array and make changes to the items in any component no problems. But when I setUpfiles with a copy of upfiles with one less item, I get an error: "TypeError: n is not a function" What did you expect to happen? The upfiles array should now be one less item all over. Are you willing to submit a pull request to fix this bug? I would not know where to start 👎

    opened by Ofer-Gal 8
  • fix: types for optional stores

    fix: types for optional stores

    What is the purpose of this pull request? (put an "X" next to item)

    [ ] Documentation update [x] Bug fix [ ] New feature [ ] Other, please explain:

    What changes did you make? (Give an overview)

    Changed the types of getStoreType, useStoreType, setStoreType and withStoreType to accept stores where some keys are optional.

    Which issue (if any) does this pull request address?

    I didn't open an issue beforehand, but I'll try to explain the issue. Imagine we have the following store:

    type User = {
      username: string;
      avatar: string | undefined;
    }
    
    type Store = {
      user: User | undefined
    }
    
    const { useStore, getStore, withStore, setStore } = createStore<Store>()
    

    The following function calls are marked as invalid by TypeScript, but do work through the Proxy:

    setStore.user.username() // Property 'username' does not exist on type 'setter<User | undefined>'.
    getStore.user.username() // Property 'username' does not exist on type 'HookDry<User | undefined>'.
    useStore.user.username() // Property 'username' does not exist on type 'Hook<User | undefined>'.
    withStore.user.username() // Property 'username' does not exist on type 'HocFunc<Store, ComponentClass<{}, any>>'.
    

    Is there anything you'd like reviewers to focus on?

    I'm not sure this is the best way to do things, and I'm also not too sure how you'd prefer your types to be for getStore, withStore and useStore for example. This PR changes those as such:

    const [username, setUsername] = getStore.user.username()
    
    typeof username = string;
    typeof setUsername = setter<string>
    typeof getStore.user = useStoreType<User> & HookDry<User | undefined>
    

    I think at least username should be string? instead of string or getStore.user should be Optional<useStoreType<User> & HookDry<User | undefined>>. The latter makes less sense to me, as technically the code runs and doesn't throw a Cannot read properties of undefined (reading 'user') error, but the fact that username might be undefined as long a one of its parent keys is undefined should be clear.

    opened by filoozom 6
  • Migrate package to TS

    Migrate package to TS

    https://github.com/teafuljs/teaful/issues/60

    It's working in progress. I have put the following comment // @ts-nocheck, but before merging this PR has to be deleted so that all types are corrected.

    • [x] Fix all TypeScript errors + remove // @ts-nocheck comments
    • [x] Fix eslint to format .ts and .tsx files
    • [x] Move types to another file
    • [x] Reduce to less than 1kb
    opened by aralroca 5
  • Add getStore helper + withStore HoC. Change callback + remove Store + add docs + add tests

    Add getStore helper + withStore HoC. Change callback + remove Store + add docs + add tests

    Work:

    • [x] Implement getStore helper
    • [x] Implement withStore HoC
    • [x] Unify callbacks to 1 callback inside the createStore BREAKING CHANGE.
    • [x] Implement callback inside useStore and withStore
    • [x] Remove Store component (now with getStore and callback change it no longer makes sense) BREAKING CHANGE.
    • [x] Tests
    • [x] Docs.
    • [x] now occupies ~~1.2kb~~ ~~1.08kb~~ 984b, should go down to less than 1kb
    opened by aralroca 5
  • add simple example of next.js

    add simple example of next.js

    What is the purpose of this pull request? (put an "X" next to item)

    [ ] Documentation update [ ] Bug fix [ ] New feature [X] Other, please explain: add a simple example of next.js

    What changes did you make? (Give an overview) add a simple example of next.js Which issue (if any) does this pull request address? no Is there anything you'd like reviewers to focus on? no

    opened by wh5938316 4
  • adding types order, moved types to package

    adding types order, moved types to package

    What is the purpose of this pull request? (put an "X" next to item)

    [ ] Documentation update [ ] Bug fix [ ] New feature [x ] Other, please explain: Add types d.ts

    What changes did you make? (Give an overview) add types based on proposition at issue #23 Which issue (if any) does this pull request address?

    Is there anything you'd like reviewers to focus on? types, test it, edge cases

    opened by danielart 4
  • Added fetch api example

    Added fetch api example

    What is the purpose of this pull request? (put an "X" next to item)

    [ ] Documentation update [ ] Bug fix [ ] New feature [ x] Other, please explain:

    What changes did you make? (Give an overview) Added an example how to fetch api and load the data in ui along with loading and error state. It would be helpful if someone want to add api state via teaful. Note: Created this via create-next-app there may be file we do not required but can be required in future so I haven' remove those

    Which issue (if any) does this pull request address? Nothing.

    Is there anything you'd like reviewers to focus on? Sample screenshot: Screenshot 2021-11-22 at 7 12 59 PM

    opened by shubhamV123 4
  • Store not getting resetting properly

    Store not getting resetting properly

    What version of this package are you using? 0.7.2

    What operating system, Node.js, and npm version? windows

    import createStore from "teaful";
    
    const initialState = {
      username: "Aral",
      cart: {
        items: []
      }
    };
    
    export const { useStore, getStore, withStore } = createStore( initialState);
    
    export default function DisplayAge() {
      const [age, setAge, resetAge] = useStore.age(45);
    
      console.log("Render DisplayAge", age);
    
      return (
        <div style={{ margin: "10px 0" }}>
          <div>{age}</div>
          <button onClick={() => setAge((s) => s + 1)}>Inc age</button>
          <button onClick={resetAge}>Reset age</button>
        </div>
      );
    }
    
    export default function ResetStore() {
      const [state, update, reset] = useStore();
    
      return (
        <>
          <button onClick={reset}>Reset all</button>
          <div>{JSON.stringify(state, undefined, 2)}</div>
        </>
      );
    }
    

    in this example i trying to reset the entire Store but it is creating age with defaultvalue as 45 but the answer shoud be undefined in global store

    bug enhancement 
    opened by yathink3 4
  • Add todolist example

    Add todolist example

    What is the purpose of this pull request? (put an "X" next to item)

    [ ] Documentation update [ ] Bug fix [ ] New feature [ x] Other, please explain: Add todolist example of use

    What changes did you make? (Give an overview) add a new folder of examples, with a todo list folder (first example)

    Which issue (if any) does this pull request address?

    Is there anything you'd like reviewers to focus on? please check the structure

    opened by danielart 4
  • Add value field to Provider for reusability.

    Add value field to Provider for reusability.

    What version of this package are you using? 0.2.1

    What problem do you want to solve? Default value of store is fixed so store's reusability is not good.

    What do you think is the correct solution to this problem? Add value option to Provider props. It will looks like original Context API's Provider. It makes breaking change (because value field of Provider is required field), so I want to know how do you think about it.

    Are you willing to submit a pull request to implement this change? After maintainer's feedback, yes.

    Example

    import createStore from "fragmented-store";
    
    const { Provider, useUsername, useAge, useStore } = createStore({
      username: "Aral",
      age: 31
    }); // It is just default values that used when hook can't find appropriate Provider.
    
    export default function App() {
      return (
        <Provider value={{
            username: "Aral",
            age: 31
        }}>
          <Title />
          <UpdateTitle />
          <Age />
          <AllStore />
        </Provider>
      );
    }
    
    opened by Lunuy 4
  • useState within an existing hook (useEffect)

    useState within an existing hook (useEffect)

    What version of this package are you using? 0.11.1

    What problem do you want to solve? Subscription (event listener / observer) without a hook.. here's the scenario:

    Within a React functional component, I have a useEffect hook which performs an expensive operation, then I use the result for a variety of other things, such as in this example of baking a cookie, then eating it:

    const CookieComponent = () => {
    
      useEffect(() => {
        let cookie
        const bakeCookie = async () => cookie = await bake('cookie')
        bakeCookie()
    
        // this is where I want teaful to watch for activity
        function eatCookie() {
          const [milk] = teafulState.milk()
         
          if (milk) eat(cookie)
    
           // I can do this instead: window.addEventListener('milk', () => eat(cookie))
        }
    }
    

    I'm trying to use teaful to replace my event bus, however I can't seem to use the subscriptions within a hook, where I currently use listeners.

    What do you think is the correct solution to this problem? Not sure, something like window.addEventListener?

    Are you willing to submit a pull request to implement this change? Yes, if I knew what needed to be done.

    Thanks! Great state management tool :)

    opened by jgentes 0
  • Conversion to useExternalStore

    Conversion to useExternalStore

    What is the purpose of this pull request? (put an "X" next to item)

    [ ] Documentation update [ ] Bug fix [ ] New feature [ ] Other, please explain:

    What changes did you make? (Give an overview)

    Which issue (if any) does this pull request address?

    Is there anything you'd like reviewers to focus on?

    Failing lines in onAfterUpdate.test.tsx:

    • [ ] 41
    • [ ] 89
    • [ ] 138
    • [ ] 215
    • [ ] 484
    opened by mrclay 0
  • Fixes #88

    Fixes #88

    • [ ] rewrite with useSyncExternalStore.

    What is the purpose of this pull request? (put an "X" next to item)

    [ ] Documentation update [x] Bug fix [ ] New feature [ ] Other, please explain:

    What changes did you make? (Give an overview)

    Require React 18, use useSyncExternalStore for more reliable subscription.

    Which issue (if any) does this pull request address?

    #88

    Is there anything you'd like reviewers to focus on?

    opened by mrclay 2
  • Cannot get new state if in conditional render ( really tricky )

    Cannot get new state if in conditional render ( really tricky )

    teaful: 0.10.0 node: v15.14.0 npm : 7.7.6

    import { useEffect, useState } from 'react'
    import createStore from 'teaful'
    import './App.css'
    import ErrorBoundary from './components/ErrorBoundary'
    
    const initialStore = {
    	number: 0,
    }
    export const { useStore } = createStore(initialStore)
    
    function App() {
    	const [show, set_show] = useState(false)
    	useEffect(() => {
    		set_show(true)
    	}, [])
    
    	return (
    		<ErrorBoundary>
    			<div className="App">
    				<AA />
    				<CC />
    
    				{/* {show && (
    					<>
    						<AA />
    						<CC />
    					</>
    				)} */}
    			</div>
    		</ErrorBoundary>
    	)
    }
    
    const AA = () => {
    	const [number] = useStore.number()
    	console.warn(number, 45678)
    
    	return number
    }
    
    const CC = () => {
    	const [number, set_number] = useStore.number()
    	if (!number) {
    		set_number((prev) => prev + 1)
    	}
    
    	return 'c'
    }
    
    export default App
    
    

    works well works fine

    BUT if I put AA and CC into conditional render and other code just stay the same:

    import { useEffect, useState } from 'react'
    import createStore from 'teaful'
    import './App.css'
    import ErrorBoundary from './components/ErrorBoundary'
    
    const initialStore = {
    	number: 0,
    }
    export const { useStore } = createStore(initialStore)
    
    function App() {
    	const [show, set_show] = useState(false)
    	useEffect(() => {
    		set_show(true)
    	}, [])
    
    	return (
    		<ErrorBoundary>
    			<div className="App">
    				{/* <AA />
    				<CC /> */}
    
    				{show && (
    					<>
    						<AA />
    						<CC />
    					</>
    				)}
    			</div>
    		</ErrorBoundary>
    	)
    }
    
    const AA = () => {
    	const [number] = useStore.number()
    	console.warn(number, 45678)
    
    	return number
    }
    
    const CC = () => {
    	const [number, set_number] = useStore.number()
    	if (!number) {
    		set_number((prev) => prev + 1)
    	}
    
    	return 'c'
    }
    
    export default App
    
    

    AA cannot get new number anymore WHY !?

    bug 
    opened by WreewanMorhee 2
  • added prettier

    added prettier

    What is the purpose of this pull request? (put an "X" next to item)

    [ ] Documentation update [ ] Bug fix [ ] New feature [ X] Other, please explain: Added prettier which will help better format the code and impose some formating rules

    What changes did you make? (Give an overview) Added prettier to the project, added prettierric.json, prettierignore files

    Which issue (if any) does this pull request address?

    Is there anything you'd like reviewers to focus on?

    opened by aliasgar55 0
Releases(0.11.0)
  • 0.11.0(Sep 1, 2022)

    What's Changed

    • add simple example of next.js by @wh5938316 in https://github.com/teafuljs/teaful/pull/83
    • Migrate package to TS by @aralroca in https://github.com/teafuljs/teaful/pull/68
    • fix: types for optional stores by @filoozom in https://github.com/teafuljs/teaful/pull/94

    New Contributors

    • @wh5938316 made their first contribution in https://github.com/teafuljs/teaful/pull/83
    • @filoozom made their first contribution in https://github.com/teafuljs/teaful/pull/94

    Full Changelog: https://github.com/teafuljs/teaful/compare/0.10.0...0.11.0

    Source code(tar.gz)
    Source code(zip)
  • 0.11.0-canary.2(Apr 9, 2022)

  • 0.11.0-canary.1(Mar 12, 2022)

  • 0.10.0(Jan 16, 2022)

    What's Changed

    • Update devDependencies by @aralroca in https://github.com/teafuljs/teaful/pull/66
    • Improve GitHub action workflow by @aralroca in https://github.com/teafuljs/teaful/pull/67
    • Add image to readme by @aralroca in https://github.com/teafuljs/teaful/pull/71
    • Replace img by @aralroca in https://github.com/teafuljs/teaful/pull/72
    • New documentation site by @siddharthborderwala in https://github.com/teafuljs/teaful/pull/32
    • Add setStore proxy by @aralroca in https://github.com/teafuljs/teaful/pull/79
    • Add test by @aralroca in https://github.com/teafuljs/teaful/pull/81

    New Contributors

    • @siddharthborderwala made their first contribution in https://github.com/teafuljs/teaful/pull/32

    Full Changelog: https://github.com/teafuljs/teaful/compare/0.9.2...0.10.0

    Source code(tar.gz)
    Source code(zip)
  • 0.9.2(Dec 5, 2021)

    What's Changed

    • Add third param to hoc type, to allow onAfterUpdate by @danielart in https://github.com/teafuljs/teaful/pull/65

    Full Changelog: https://github.com/teafuljs/teaful/compare/0.9.1...0.9.2

    Source code(tar.gz)
    Source code(zip)
  • 0.9.1(Dec 3, 2021)

    What's Changed

    • Fix getStore type + fix copy-types by @aralroca in https://github.com/teafuljs/teaful/pull/58

    Full Changelog: https://github.com/teafuljs/teaful/compare/0.9.0...0.9.1

    Source code(tar.gz)
    Source code(zip)
  • 0.9.0(Dec 2, 2021)

    Teaful Devtools is available on 0.9.0! 🎉🥳 To know more about it: https://github.com/teafuljs/teaful-devtools/

    and.. TypeScript types are available on 0.9.0 🚀

    What's Changed

    • adding types order, moved types to package by @danielart in https://github.com/teafuljs/teaful/pull/52
    • Update docs by @aralroca in https://github.com/teafuljs/teaful/pull/53
    • Update example deps by @aralroca in #46
    • Add a simple example of counter with ESM by @aralroca in #47
    • Added fetch api example by @shubhamV123 in #48
    • Allow 3th party libraries to expand Teaful with extras by @aralroca in #50
    • Remove unnecessary bits by @aralroca in #54

    Full Changelog: https://github.com/teafuljs/teaful/compare/0.8.1...0.9.0

    Source code(tar.gz)
    Source code(zip)
  • 0.8.1(Nov 30, 2021)

    What's Changed

    • Update example deps by @aralroca in https://github.com/teafuljs/teaful/pull/46
    • Add a simple example of counter with ESM by @aralroca in https://github.com/teafuljs/teaful/pull/47
    • Added fetch api example by @shubhamV123 in https://github.com/teafuljs/teaful/pull/48
    • Allow 3th party libraries to expand Teaful with extras by @aralroca in https://github.com/teafuljs/teaful/pull/50
    • Remove unnecessary bits by @aralroca in https://github.com/teafuljs/teaful/pull/54

    New Contributors

    • @shubhamV123 made their first contribution in https://github.com/teafuljs/teaful/pull/48

    Full Changelog: https://github.com/teafuljs/teaful/compare/0.8.0...0.8.1

    Source code(tar.gz)
    Source code(zip)
  • 0.9.0-canary.2(Nov 26, 2021)

  • 0.9.0-canary.1(Nov 23, 2021)

    What's Changed

    • Update example deps by @aralroca in https://github.com/teafuljs/teaful/pull/46
    • Add a simple example of counter with ESM by @aralroca in https://github.com/teafuljs/teaful/pull/47
    • Added fetch api example by @shubhamV123 in https://github.com/teafuljs/teaful/pull/48

    New Contributors

    • @shubhamV123 made their first contribution in https://github.com/teafuljs/teaful/pull/48

    Full Changelog: https://github.com/teafuljs/teaful/compare/0.8.0...0.9.0-canary.1

    Source code(tar.gz)
    Source code(zip)
  • 0.8.0(Nov 14, 2021)

    What's changed

    • Add todolist example by @danielart in https://github.com/teafuljs/teaful/pull/40
    • Remove confusing reset feature by @aralroca in https://github.com/teafuljs/teaful/pull/44

    Breaking changes

    • One this breaking change for this version: Remove confusing reset feature https://github.com/teafuljs/teaful/pull/44

      Reset function is removed. It's not responsibility of the library. In this way:

      Pros:

      • Removing confusion as to what exactly the reset function did. Simplifying and making it easier to understand.
      • Fewer things in memory. Thus it is not necessary to save the store + initialStore. Only the store. This improves performance for applications with very large stores.
      • Tiniest. Supporting reset made it take up more bits.
      • Removing a feature that did not add value to the library, since the same could be done with the setter.
      • More flexibility to the user to reset with the setter where the user wants: last value, last value fetched to an API, initial value...

      Cons:

      • The user will be responsible for controlling the reset. That is, save the value in memory.

        Example:

        const [value, setValue] = useStore.value()
        const lastValue = useRef(value)
        
        // After some changes, is possible to reset with:
        setValue(lastValue.current)
        

        Another example:

         const initialStore = { count: 0 }
         export const { getStore } = createStore(initialStore)
         const [,setStore] = getStore()
        
         export const reset = () => setStore(initialStore)
        
         import { reset } from './store'
        
         function ResetStore() {
           return <button onClick={reset}>Reset store</button>
        }
        

    Full Changelog: https://github.com/teafuljs/teaful/compare/0.7.2...0.8.0

    Source code(tar.gz)
    Source code(zip)
  • 0.8.0-canary.1(Nov 12, 2021)

  • 0.7.2(Nov 8, 2021)

    What's Changed

    • Fix subscription when the path change by @aralroca in https://github.com/teafuljs/teaful/pull/37

    Full Changelog: https://github.com/teafuljs/teaful/compare/0.7.1...0.7.2

    Source code(tar.gz)
    Source code(zip)
  • 0.7.1(Nov 8, 2021)

    What's Changed

    • fix : curly bracket on createStore callback function by @nekonako in https://github.com/teafuljs/teaful/pull/33
    • Fix microbundle bug by @aralroca in https://github.com/teafuljs/teaful/pull/35

    New Contributors

    • @nekonako made their first contribution in https://github.com/teafuljs/teaful/pull/33

    Full Changelog: https://github.com/teafuljs/teaful/compare/0.7.0...0.7.1

    Source code(tar.gz)
    Source code(zip)
  • 0.7.0(Nov 5, 2021)

    ~Fragstore~ is renamed to Teaful

    CHANGES

    • fix: update README.md #24 by @niexq
    • fix: eslint warning and remove dist added to eslintignore #27 by @niexq

    BREAKING CHANGES

    • Change of name. Fragstore now is Teaful:
    • Convert onAfterUpdate to be more easy, tiny and powerful #22 by @aralroca
      • Inside the PR there are docs how to migrate from old to new version
    Source code(tar.gz)
    Source code(zip)
  • 0.6.0(Nov 1, 2021)

    0.6 release 🚀

    We are taking advantage of the fact that we are in an early stage (0.x) to evolve quickly the library trying to make everything more and more tiny, easy and powerful.

    Sorry for the breaking changes and if you have any questions you can write inside GitHub Discussions.

    The library is becoming more and more stable and we will make less breaking changes, and from version 1.0 onwards we will follow the semantic version 100%.

    🚨 I have to announce that after having changed the name of the library from fragmented-store to fragstore, we are forced to change it again to avoid problems with a company called fragstore that we didn't know about.

    We have not changed the name for this version as it already has some breaking changes. But we will change it for the next version and it will be the only breaking change for the future 0.7 version.

    We will make it so that when installing the latest fragstore package in npm it will make a warning saying so, but I prefer to announce it.

    👉 Subscribe to this discussion to find out about the name change.

    New features

    • Add getStore helper #19 (by @aralroca) - Read more here
    • Add withStore HoC #19 (by @aralroca) - Read more here
    • Add callbacks inside useStore, getStore and withStore #19 (by @aralroca) - Read more here
    • Update all the docs #19 (by @aralroca)
    • Add more tests #19 (by @aralroca)

    Breaking changes

    • Remove Store component (now with getStore and callback change it no longer makes sense) #19 (by @aralroca)
    • Callbacks are unified (1 function instead of an object of functions) #19 (by @aralroca)

    How to migrate

    1. Remove this component
    <Store store={initialStore} callbacks={{ cart: onCartUpdate }}>
    </Store>
    
    1. Use the store variable inside createStore:
    const { useStore, getStore } = createStore(initialStore)
    
    // Or if you need it to loading dynamically use the getStore helper:
    
    const [, setStore] = getStore()
    setStore(store => ({ ...store, ...initialStore }))
    
    1. Use a callback inside createStore:
    const { useStore, getStore } = createStore(initialStore, onStoreUpdate)
    
    function onStoreUpdate({ path, ...rest }) {
      if(path.startsWith('cart')) onCartUpdate(...rest)
    }
    
    // Or if you want the event to be cleared when the component unmounts, use useStore:
    
    const [cart, setCart] = useStore.cart(initialCartValue, onCartUpdate)
    
    Source code(tar.gz)
    Source code(zip)
  • 0.6.0-canary.2(Oct 30, 2021)

  • 0.6.0-canary.1(Oct 30, 2021)

  • 0.5.0(Oct 22, 2021)

    • Some improvements: rename Provider to Store, add types + fix eslint #14 (by @aralroca and @danielart)

    BREAKING CHANGES

    • We renamed Provider to Store because after the reimplementation it is no longer mandatory to use it because it is no longer a provider, it only makes sense to redefine the store and callbacks. In the future, it will surely have more features.
    Source code(tar.gz)
    Source code(zip)
  • 0.4.1(Oct 14, 2021)

  • 0.4.0(Oct 13, 2021)

    • Rename lib to Fragstore
    • Allow fragmented store in more levels
    • Add callbacks
    • Allow store and callbacks to Provider

    BREAKING CHANGES

    • Instead of useUsername now is useStore.username(). This allow multi fragment userStore.cart.price(). And for all store is useStore().
    Source code(tar.gz)
    Source code(zip)
  • 0.3.0(Sep 30, 2021)

    • Introduce callbacks #3 (by @aralroca)

    This introduces callbacks, that are executed for any property change. Is useful for example to fetch data to an endpoint after the state change, and roll back the state if the request fails.

    const initialState = {
      quantity: 2
    }
    
    // This is new
    const callbacks = {
      quantity: (newValue, prevValue, setValue) => {
        // Update quantity from API
        fetch('/api/quantity', { method: 'POST', body: newValue })
         // Revert state change if it fails
         .catch(e => setValue(prevValue))
      }
    }
    
    const { Provider, useQuantity } = createStore(initialState, callbacks)
    
    Source code(tar.gz)
    Source code(zip)
  • 0.2.1(Sep 24, 2021)

  • 0.2.0(Sep 24, 2021)

  • 0.1.4(Sep 22, 2021)

  • 0.1.3(Mar 12, 2021)

  • 0.1.0(Mar 11, 2021)

Owner
Teaful
Tiny, easy and powerful React state management library
Teaful
A tiny state manager for React, Svelte, Vue and vanilla JS

dotto.x Dotto.x is a tiny state manager for React, Svelte, and vanilla JS. Lightweight. Core is less than 135 bytes (minified and gzipped). Zero depen

null 108 Nov 2, 2022
A tiny, reactive JavaScript library for structured state and tabular data.

A JavaScript library for structured state. Using plain old JavaScript objects to manage data gets old very quickly. It's error-prone, tricky to track

tinyplex 1.4k Dec 30, 2022
An event-based global state management tool for vue, react, mini-program, ect.

hy-event-store An event-based global state management tool for vue, react, mini-program, etc. 一个基于事件的全局状态管理工具,可以在Vue、React、小程序等任何地方使用。 设计灵感 在项目中找到一个更加

null 260 Jan 2, 2023
micro reactive state management - just for fun

effectus micro reactive state management - just for fun Install $ yarn add effectus Usage import { effect, signal } from 'effectus' const [name, set

Hector Zarco 1 Dec 7, 2021
A tiny library aims to parse HTML to Figma Layer Notation and do it well.

TSDX User Guide Congrats! You just saved yourself hours of work by bootstrapping this project with TSDX. Let’s get you oriented with what’s here and h

青岚 5 Dec 28, 2022
A tiny ( 1KB) reactivity library

influer A tiny (< 1KB) reactivity library yarn add influer Introduction influer is a tiny reactivity library. Exported in ESM, CJS, and IIFE, all < 1K

Tom Lienard 7 Jul 13, 2022
A tiny, blazing fast view library that creates reactive Web Components

dlite A tiny, blazing fast view library that creates reactive Web Components ?? Complete documentation https://dlitejs.com ?? Introduction dlite creat

Adam Hill 20 Feb 25, 2023
RTConnect is an easy-to-use React component library that enables developers to set up live, real-time video calls, between multiple connected peers on different computers

RTConnect is an easy-to-use React component library that enables developers to set up live, real-time video calls, between multiple connected peers on different computers in a React app.

OSLabs Beta 64 Dec 27, 2022
🔍 Holmes is a 0 config, fast and elementary state orchestrator for React

React Holmes ?? - Elementary State Orchestrator for React ?? Holmes is a 0 config, fast and elementary state orchestrator for React. Holmes has a very

null 49 Oct 15, 2022
Immutable state for React.js

react-cursor Immutable state for React.js react-cursor hello-world in a fiddle What is react-cursor Cursors are a tool for working with recursive or d

Dustin Getz 1k Dec 12, 2022
Centrally manage state for React applications with CSP

State Trooper Install npm npm install state-trooper Yarn yarn add state-trooper Example Usage Call StateTrooper.patrolRunLoop in your route handler/ma

Swipely 15 May 14, 2022
A minimum and lightweight external store for React and React Native application

Reactive store - a minimum and lightweight external store for React and React Native application Table of contents Features Installation Usage 3.1 Cre

Hòa Võ 7 Nov 21, 2022
Maple.js is a React webcomponents based framework mixing ES6 with Custom Elements, HTML Imports and Shadow DOM. It has in-built support for SASS and JSX, including a Gulp task for vulcanizing your project.

Heroku: http://maple-app.herokuapp.com/ npm: npm install maple.js Bower: bower install maple.js Maple is a seamless module that allows you to organise

Adam Timberlake 430 Dec 23, 2022
React, React Native and Vue UI components for building data-driven apps with Elasticsearch

Reactive Search UI components library for Elasticsearch: Available for React, Vue and React Native Read how to build an e-commerce search UI a.) with

appbase.io 4.7k Jan 4, 2023
⚡️ Lightning-fast search for React and React Native applications, by Algolia.

React InstantSearch is a library for building blazing fast search-as-you-type search UIs with Algolia. React InstantSearch is a React library that let

Algolia 2k Dec 27, 2022
React ESI: Blazing-fast Server-Side Rendering for React and Next.js

React ESI: Blazing-fast Server-Side Rendering for React and Next.js React ESI is a super powerful cache library for vanilla React and Next.js applicat

Kévin Dunglas 633 Jan 5, 2023
CSS media queries in react - for responsive design, and more.

react-responsive Information Package react-responsive Description Media queries in react for responsive design Browser Version >= IE6* Demo The best s

contra 6.5k Dec 30, 2022
A performant, scalable and pluggable approach to instrumenting your React application.

react-i13n react-i13n provides a performant, scalable and pluggable approach to instrumenting your React application. Typically, you have to manually

Yahoo 369 Dec 25, 2022
Configure and build views using JSON schemas mapped to React components

react-json-schema npm install react-json-schema Construct React elements from JSON by mapping JSON definitions to React components. Use react-json-sch

Club OS 161 Dec 22, 2022