The simple but very powerful and incredibly fast state management for React that is based on hooks

Overview

Hookstate

The simple but very powerful and incredibly fast state management for React that is based on hooks.


Why?Docs / SamplesDemo applicationPluginsRelease notes

Preface

Hookstate is a modern alternative to Redux, Mobx, Formik without boilerplate. It is simple to learn but very flexible to use. It has got impressive performance and predictable behavior.

Any questions? Just ask by raising a github ticket.

Why Hookstate

hookstate.js.org

Migrating to version 3

hookstate.js.org/docs/migrating-to-v3

Documentation / Code samples

hookstate.js.org/docs/getting-started

Demo application

hookstate.js.org/demo-todolist

Development tools

hookstate.js.org/docs/devtools

Plugins / Extensions

hookstate.js.org/docs/extensions-overview

API reference

hookstate.js.org/docs/typedoc-hookstate-core

Comments
  • Add next js demo. Was: Warning when using Hookstate with Next.JS

    Add next js demo. Was: Warning when using Hookstate with Next.JS

    Hello,

    I am receiving a warning when I use Hookstate with NextJS.

    Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format. This will lead to a mismatch between the initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client. See https://reactjs.org/link/uselayouteffect-ssr for common fixes.

    I am not able to find any instruction on how to use Hookstate with Next.

    Any help is greatly appreciated.

    enhancement 
    opened by ermaliii 39
  • Question: Example Application

    Question: Example Application

    Any applications in the wild currently using this that you're aware of? The library seems really interesting and all the demo snippets are fantastic, but coming from a redux background I'm trying to wrap my head around how it'd be implemented in a real-world application. Specifically in terms of architecture and recommended design patterns.

    question 
    opened by eddielee394 32
  • Add StateLink.merge from Mutate plugin (WAS: Using Mutate for stateRef)

    Add StateLink.merge from Mutate plugin (WAS: Using Mutate for stateRef)

    createStateLink({a: 1}).with(Mutate) does not work, throws an error Is it supposed to work like this ? Also, I see in the Mutate example: Mutate(state) But in validation example I see Validation(state) but also .with(Validation)

    I would like to do it once with(mutate) and use Mutate functions everywhere

    useStateLink(FieldStore).with(Mutate) doesn't work as well

    opened by lishine 28
  • Question: Subscribing to nested changes at a parent level

    Question: Subscribing to nested changes at a parent level

    Hi avkonst,

    Amazing library you've built here. It looks exactly like the type of thing I was imagining. I'm going through evaluating libraries to try to get to a less hacky way of implementing state.

    I had a question on a specific use case that's not obvious to me if hookstate would handle well (or could have a plugin written to handle).

    I have a large recursive nested structure, basically a workflow of questions and answers which lead to more questions then more answers and so on. I've built a hacked-together solution with regular react components (well, preact), where the top-level component has the entire tree as a state variable, passing down sections of the tree to its children, and then each nested component has an oninput that mutates its section of the tree directly and then calls forceUpdate(). This works because the tree of Components mirror the tree of the state, so forceUpdate() correctly cascades for the corresponding subtree. I'm sure many would cry at this implementation, but it works well enough.

    In hookstate, it looks like nested combined with the usage of scoped states would work well enough as a replacement here. The subtree would get passed in to each component, and it would be able to set on the scoped state oninput, which should then update the state and trigger a render.

    Then, I support undo/redo functionality, by changing the top-level state tree into a proxy, so when anything is set or delete, I store a snapshot of the whole tree, and then undo/redo buttons just setState the whole tree with the snapshot, so everything renders as normal.

    This is then where I'm not sure how I would implement this snapshot/apply functionality in hookstate. I think what I would want is some way to know at the top-level (or maybe even any nested level) that there is a change down the line, so that I can know to store a snapshot. There is a Touched plugin demo that shows the parent knows when it was touched, but do those just store a one-time flag for the first change? Or is there some way to subscribe to any changes? This also makes me think about things like time travel, though I think maybe your library specifically chose to eschew this functionality.

    Maybe I'm thinking about this the wrong way, and it would just be most straightforward to add a transform for saveSnapshot and have the Component oninput function just call that after state set.

    Any thoughts/advice?

    documentation 
    opened by jmchuster 24
  • Hookstate error 111 doesn't exist

    Hookstate error 111 doesn't exist

    My application has thrown a hookstate error 111 with the link https://hookstate.js.org/docs/exceptions/#hookstate-111. This does not exist in the documentation.

    documentation 
    opened by mattangus 23
  • Support setting/getting undefined paths

    Support setting/getting undefined paths

    Use Case:

    There is a form object (say, DTO for REST or GraphQL parameter), that has nested optional arguments:

    interface Argument {
      root: string;
      nested?: {child: string}
    }
    

    Currently, in order for hook state to support writing to nested objects which do not exist yet, you have to put empty objects in the state:

    const state = useState({root: 'foobar', nested: {} as any});
    

    If you do not insert an empty object, code like this will break:

    
    const scoped = useState(state);
    
    // nested = undefined error
    scoped.nested.child.set('wee');
    

    Currently my project has these empty objects defined everywhere and this is something that can easily bite you at runtime depending on how things are setup.

    Is it feasible for the library to support constructing missing object paths?

    enhancement question hookstate-4 
    opened by parisholley 23
  • Allow State to be used in dependency lists for React.useEffect, React.useMemo, etc. and as prop for React.memo

    Allow State to be used in dependency lists for React.useEffect, React.useMemo, etc. and as prop for React.memo

    First of all, thank you for this awesome lib!

    I found myself having difficulties trying to find the right way to implement some state updates efficiently, and the following reflexion arose:

    What about using Immer? Thanks to its support for patches, would allow this lib to support a wider range of operations, on all sort of data types. set and merge methods could be replaced by produce, and plugins could leverage patches (which would be useful for my plugin).

    I really like the whole intelligent proxy system that is currently in place, and I honestly think that putting Immer in there would make the whole lib even better!

    I wonder if this is something possible? Or is there some limitations or willingness to keep everything inside hookstate itself?

    enhancement question hookstate-4 
    opened by magne4000 22
  • Documentation for Devtools for Hookstate (released: @hookstate/devtools)

    Documentation for Devtools for Hookstate (released: @hookstate/devtools)

    Here is the prototype of devtools. It injects to Hookstate as a standard plugin, which submits state changes to Redux DevTools. Should I finalise it? Please comment / react to express your interest.

    Here is the screenshot: image

    enhancement documentation plugin 
    opened by avkonst 19
  • Unintentional Synching of Parent and Child Components States

    Unintentional Synching of Parent and Child Components States

    I'm sending an object state as a prop down to the child to be set as an initial value of the child state. Now I'm using the recommended way to do this as highlighted in 102 exception.

    Then I simply mutates the child state but find "initial value" prop getting updated too. This is undesirable and I can't seem to understand the reason for this behavior.

    I'm using v3.0.13

    Here's the code snippet:

    const ParentComponent = () => {
       const parentState = useState({ a: [1, 2], b: [2, 3], c: [4, 5] });
      
      return (
         <ChildComponent  
               initalVal={JSON.parse(JSON.stringify(parentState.get()))}
          />
      );
    };
    
    const ChildComponent = ({ intialVal }) => {
       const childState = useState(initialVal);
    
       const updateChildState = () => {
             childState.a.set(p => {
                    p.splice(1, 1);
                    return p;
             });
       }; 
    
      return (
           <Text>Initial A : {JSON.stringify(initalVal.a)}</Text>
           <Text>Updated Child State A: {JSON.stiringify(childState.a.get())}</Text>
           <Button
                ...
                onPress={updateChildState}
           />
      );
    };
    

    Expecting: Initial A: [1, 2]

    Actually getting: Initial A: [1]

    question 
    opened by waleedshkt 18
  • Simpler and more intuitive way to do attach(Downgrade)

    Simpler and more intuitive way to do attach(Downgrade)

    We tried to implement hookstate into our serverless app and we needed to send objects from the state to the firebase database. However, in order to do so, we needed to do the following:

    const data = myState.attach(Downgraded).get()
    

    This has one main issue, which is the reason we scrapped hookstate in favor of something else. It's not intuitive, a person who hasn't read the docs a couple of times won't know what is going on. It's also really counter intuitive how .get() returns you a proxy, which can't be used directly in JSON API request. I get that there has to be a reason to force everything to need Downgraded attached to get the pure value, but the way it has to be done is ugly. I suggest creating a function toObject() which can attach Downgraded internally, but it just has better code flow. The example from above would become the following:

    const data = myState.toObject().get()
    

    or

    const data = myState.get().toObject()
    
    enhancement question hookstate-4 
    opened by bmutafov 16
  • Proposal to improve Validation plugin

    Proposal to improve Validation plugin

    I was initially stuck because I wanted my navigation to show badges when "tabs" had validation issues, but didn't want to re-render all the tabs on change. Initially I was doing

    state.array.forEach(a => {
      Validation(a.name).validate(a => a.length > 0, 'Name required');
    });
    

    The problem with the above though is it will now subscribe my tab/navigation to any validation failures and cause a full re-render of all children.

    Once I dug into the code I realized I could write it like this:

    Validation(a.array[0].name).validate(a => a.length > 0, 'Name required');
    

    Which actually ends up wildcarding behind the scenes, but not clear from the syntax. At the very least would be helpful to document this current behavior, but I think a more idiomatic syntax would be:

    Validation(a.array).forEach(a => {
    a.name.validate(a => a.length > 0, 'Name required');
    });
    
    enhancement plugin hookstate-4 
    opened by parisholley 16
  • Array management hits call stack limit

    Array management hits call stack limit

    I want to get an array as an array, not as an ImmutableObject and the unique thing that I thought was map the "array" to get an array, but when I use any function that I can use with arrays it hits the call stack limit: RangeError: Maximum call stack size exceeded.

    This is my code:

    // state.ts
    
    const defaultData: RPC = {
    	name: '',
    	app_id: '',
            // ...
    	buttons: [
    		{ label: '', url: '' },
    		{ label: '', url: '' }
    	]
    };
    
    const state = {
    	// ...
    	formData: hookstate<RPC>(defaultData),
    };
    
    export default state;
    
    // Main.tsx
    
    const data = useHookstate(state.formData);
    // ...
    <div onClick={() => console.log(data.get().buttons.map((e) => e.label))}> Button </div>
    

    I'm doing something wrong or is a bug? Thanks

    opened by gatomo-oficial 0
  • fix(localstored): mark

    fix(localstored): mark "engine"-option as optional

    The documentation and code mark the "engine"-prop as optional. However, the TypeScript type enforces it to be required, therefore any consumer who tries to omit this prop and use the default built-in engine gets a TypeScript error.

    This change updates the argument-type of localstored, such that the "engine"-option becomes optional. Which allows a consumer to use the default build-in LocalStorage-based engine.

    Old behaviour: image

    Documentation examples show usage of this plugin without explicitly setting an implementation for the engine: https://github.com/avkonst/hookstate/blob/master/docs/index/src/examples/plugin-localstored.tsx

    opened by Addono 1
  • Partial Updates (Scoped State) with Classes

    Partial Updates (Scoped State) with Classes

    Is there a way to get partial updates working when using classes instead of plain objects?

    Consider the following example:

    import React from 'react';
    import { useHookstate } from "@hookstate/core";
    
    class AppState {
      constructor() {
        this.a = 0;
        this.b = 0;
      }
    }
    
    export function App() {
      const state = useHookstate(new AppState())
      return (
        <div>
          <p>{new Date().toISOString().toString()}: {state.a.get()}</p>
          <Component count={state.b} />
        </div>
      );
    }
    
    function Component(props) {
      const count = useHookstate(props.count)
      return (
        <>
          <p>{new Date().toISOString().toString()}: {count.get()}</p>
          <button onClick={() => count.set(count => count + 1)}>Count</button>
        </>
      );
    }
    

    Here, both the parent component and the child component get updated when the button is clicked. When new AppState() is replaced with { a: 0, b: 0 }, only the child component gets updated. Is there a way to get the behavior from the plain object state while using a class object? I'm fully aware that Hookstate won't be able to catch every update when more advanced class features like getters and methods are used, but I'm willing to handle these cases myself.

    I would like to use Hookstate to model the UI around an existing app state instead of duplicating the state for the UI, but this won't work if on every change the whole UI gets updated. Most of the app state behaves like a plain object, the class is mostly used for data validation and stuff like that.

    The documentation of Hookstate says, that for non-plain objects, get() gets replaced with get({ noproxy: true }). I'm fine with that, but I think in the example above something else is going on because when I replace new AppState() with { a: 0, b: 0 } and both get calls with get({ noproxy: true }), the partial update still works.

    Example Class State: https://playcode.io/1047463 Example Plain Object State: https://playcode.io/1047469 Example Plain Object State with { noproxy: true }: https://playcode.io/1047470

    opened by zroug 0
  • Localstored raise type error at 4.0.0

    Localstored raise type error at 4.0.0

    Hi, Thank you for working v4.0.0

    When I update localstored 4.0.0-rc21 to 4.0.0 localstored raise type error.

    type Foo = {
      name: string
    }
    const initialState = {
      name: '',
    }
    
    export const useNameState = () => {
      const state = useHookstate<Foo>(
        initialState,
        localstored({
          key: 'registration-key',
        })
      )
      return state
    }
    
    error TS2345: Argument of type '{ key: string; }' is not assignable to parameter of type '{ key?: string | undefined; engine: Stor
    eEngine; initializer: () => Promise<Foo>; }'.
    
    Screenshot 2022-12-26 18 23 16

    I refered with document. https://hookstate.js.org/docs/extensions-localstored

    localstored 4.0.0-rc21 works fine.

    Regards.

    opened by heavenshell 1
  • Typescript error when defining a state with OR operator

    Typescript error when defining a state with OR operator

    Hey there,

    I'm have this code:

    import { hookstate, useHookstate } from '@hookstate/core';
    
    interface DraggableStoreNotDraggingState {
      dragging: false;
      waiting: false;
    }
    
    interface DraggableStoreWaitingState {
      dragging: false;
      waiting: true;
      waitingPoint: { x: number; y: number };
    }
    
    export interface DraggableStoreDraggingState {
      dragging: true;
      waiting: false;
      startingPoint: { x: number; y: number };
      currentPoint: { x: number; y: number };
    }
    
    type DraggableStoreState =
      | DraggableStoreDraggingState
      | DraggableStoreNotDraggingState
      | DraggableStoreWaitingState;
    
    export const InitalState = Object.freeze({
      dragging: false as false,
      waiting: false as false,
    });
    
    const DraggableStore = hookstate<DraggableStoreState>({ ...InitalState });
    
    if (DraggableStore.waiting.get()) {
      // typescript error
      DraggableStore.waitingPoint.get();
    }
    

    Basically typescript says that waitingPoint is undefined though waiting is true.

    How should this be done?

    Thanks

    opened by emroot 0
  • Challenges updated nested values (proxy error)

    Challenges updated nested values (proxy error)

    I'm hoping to get some guidance as I am struggling to get this to work and not sure if it is a bug (like in #286).

    I have the following state:

    {
      "item": {
        "name": "item name",
        "subItems": [
          {"name": "subitem1 name"},
          {"name": "subitem2 name"}
        ]
      },
      "options": {
        "key": "value"
      }
    }
    

    When I try to replace the items found in item.subItems I get the following error in the console:

    TypeError: 'get' on proxy: property '0' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected '#<Object>' but got '#<Object>')
    

    I can update the parent item without issue.

    state.item.merge({"name":"new item name"}) 
    

    I can empty the subItem child without issue.

    state.item.subItems.set([]) 
    

    I can modify items in the subItem array without issue.

    state.item.subItems[1].merge({"name":"new subitem2 name"}) 
    

    I cannot replace the contents of the subItems array with new items without the error above.

    state.item.subItems.set([
      {"name": "different subitem1 name"},
      {"name": "different subitem2 name"}
    ]) 
    

    Why is this happening?

    ::edit::

    So it turns out that there must be something wrong with the data I'm retrieving and trying to add to the state. The following works without issue. Logging the array to the console doesn't reveal anything out of the ordinary, so not sure why it is behaving like this.

            state.item.subItems.set([]);
            for (const subItem of loadedSubItems) {
              const { name } = subItem;
              state.item.subItems.merge([{ name }]);
            }
    
    opened by kisonay 0
Releases(v3.0.12)
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
🔍 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
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
⚡️ 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
A blazing fast, dependency free, 1kb runtime type-checking library written entirely in typescript, meant to be used with it.

A blazing fast, dependency free, 1kb runtime type-checking library written entirely in typescript, meant to be used with it.

Gabriel Vaquer 90 Dec 9, 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
Simple and elegant component-based UI library

Simple and elegant component-based UI library Custom components • Concise syntax • Simple API • Tiny Size Riot brings custom components to all modern

Riot.js 14.7k Jan 8, 2023
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
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
Lightweight react-like library. Support for asynchronous rendering and hooks.

Recept · Lightweight react-like library. Like the name, this project is mainly based on the architectural idea of react, which can feel react more int

RuiLin Dong 52 Sep 17, 2022
Modern file uploading - components & hooks for React

Modern file-upload components & hooks for React. Contents Intro Documentation Installation Packages Examples Important Concepts Resumable Uploads UMD

rpldy 768 Dec 28, 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
Demo app for refine.dev integration, a react-based framework for rapid building of internal tools.

Demo app for refine.dev integration, a react-based framework for rapid building of internal tools.

Permify 7 Apr 28, 2022
A reactive filesystem interface based on Vue 3 reactivity system.

A reactive filesystem interface based on Vue 3 reactivity system.

Guillaume Chau 37 Oct 8, 2022
:postbox: A simple and customizable React notifications system

Reapop A simple and customizable React notifications system Summary Compatibility Demo Installation Integration & usage With React & Redux With React

Louis Barranqueiro 1.4k Jan 5, 2023
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