All Projects → Attrash-Islam → react-wisteria

Attrash-Islam / react-wisteria

Licence: MIT License
Managing the State with the Golden Path

Programming Languages

javascript
184084 projects - #8 most used programming language
SCSS
7915 projects

Projects that are alternatives of or similar to react-wisteria

Use Global Context
A new way to use “useContext” better
Stars: ✭ 34 (+88.89%)
Mutual labels:  state-management, state, context
React Workshop
⚒ 🚧 This is a workshop for learning how to build React Applications
Stars: ✭ 114 (+533.33%)
Mutual labels:  state-management, state, context
Alveron
Elm & Reason inspired state management for React
Stars: ✭ 57 (+216.67%)
Mutual labels:  state-management, state, context
Contextism
😍 Use React Context better.
Stars: ✭ 141 (+683.33%)
Mutual labels:  state-management, state, context
Yewdux
Redux-like state containers for Yew apps
Stars: ✭ 58 (+222.22%)
Mutual labels:  state-management, state, context
fast-cartesian
Fast cartesian product
Stars: ✭ 51 (+183.33%)
Mutual labels:  functional, lodash, lodash-fp
closeable-map
Application state management made simple: a Clojure map that implements java.io.Closeable.
Stars: ✭ 42 (+133.33%)
Mutual labels:  state-management, state
teaful
🍵 Tiny, easy and powerful React state management
Stars: ✭ 638 (+3444.44%)
Mutual labels:  state-management, state
restate
A redux fractal state library 🕷
Stars: ✭ 55 (+205.56%)
Mutual labels:  state-management, state
particule
Fine-grained atomic React state management library
Stars: ✭ 31 (+72.22%)
Mutual labels:  state-management, state
use-app-state
🌏 useAppState() hook. that global version of setState() built on Context.
Stars: ✭ 65 (+261.11%)
Mutual labels:  state-management, context
riduce
Get rid of your reducer boilerplate! Zero hassle state management that's typed, flexible and scalable.
Stars: ✭ 14 (-22.22%)
Mutual labels:  state-management, state
rudash
Rudash - Lodash for Ruby Apps
Stars: ✭ 27 (+50%)
Mutual labels:  functional, lodash
react-context-io
Share state with ease.
Stars: ✭ 22 (+22.22%)
Mutual labels:  state, context
reactube-client
A clone Youtube Web Player using React Provider Pattern, React Context and Typescript
Stars: ✭ 92 (+411.11%)
Mutual labels:  state-management, context
jedisdb
redis like key-value state management solution for React
Stars: ✭ 13 (-27.78%)
Mutual labels:  state-management, state
concave
🧐 Lens-like state management (for React).
Stars: ✭ 13 (-27.78%)
Mutual labels:  state-management, state
vuex-but-for-react
A state management library for React, heavily inspired by vuex
Stars: ✭ 96 (+433.33%)
Mutual labels:  state-management, state
RxReduxK
Micro-framework for Redux implemented in Kotlin
Stars: ✭ 65 (+261.11%)
Mutual labels:  state-management, state
PageStatusTransformer
A low invasive state management on Android
Stars: ✭ 12 (-33.33%)
Mutual labels:  state-management, state

React Wisteria

react-wisteria is a library that utilize the Golden Path Functional setter API into a multiple state stores that makes it easier to work with state. Get your state and update it without the need for selectors, actions, reducers, types, etc.

Wisteria

Wisteria is a genus of flowering plants in the legume family, Fabaceae, that includes ten species of woody climbing bines that are native to China.

image

Installation

npm i react-wisteria --save

Usage

Let's build a counter application

Apr-23-2020 11-48-06

This application has 3 components (Counter, Display and Controls) with <Display/> and <Controls/> being the children of <Counter/> (the Root component of our app).

First we wrap our Root component (<Counter/>) like this:

import { useCreateStore, StoreProvider } from 'react-wisteria';

const Counter = (props) => {
    // Here we create our global store. Wisteria goes with a "store per feature" way of doing things
    // because of that we've stores in the naming but one can create a single store and pass it.
    // All the stores creation should be in the higher level component (aka <App/>).
    const stores = useCreateStores([{ name: 'my-store', initialState: props }]);

    return (
        // Here we pass the store to the `StoreProvider`
        <StoreProvider stores={stores}>
            <div className="counter">
                <Display/>
                <Controls/>
            </div>
        </Provider>
    );
};

export default Counter;

As you can see, the context is being sent to Provider as part of its options.

Let us now render our Root component with its initial props (only the current counter value):

export const CounterAt_0 = () => {
    return (<Counter count={0}/>);
};

The <Display/> component, which is a child of Root, can now reach the state easily.

import { connect, useWisteriaStore, useWisteriaStateSlice } from 'react-wisteria';

const Display = ({ count }) => {
    return (
        <div className="display">
            {count}
        </div>
    )
};

const useStateToProps = () => {
    const myStore = useWisteriaStore('my-store'); // Worth using constants so you can track dependencies between stores easily.

    // Passing only one param will get the whole context but it's not preferred due to performance issues.
    // Also you can do a deep access to properties (e.g. count.value.a) - The component will render only if this deep path has changed.
    const count = useWisteriaStateSlice(myStore, 'count');

    return {
        count
    }
};

export default connect(useStateToProps)(Display);

In our second child, <Controls/>, we want to update the state:

import { connect, useWisteriaStore, useWisteriaStateUpdater } from 'react-wisteria';

const Controls = ({ onAddition, onDecrement }) => {
    return (
        <div className="controls">
            <div className="control" onClick={onAddition}>+</div>
            <div className="control" onClick={onDecrement}>-</div>
        </div>
    )
};

const useStateToProps = () => {
    const myStore = useWisteriaStore('my-store');
    const setContext = useWisteriaStateUpdater(myStore);

    const onAddition = () => {
        setContext('count', (count) => count + 1);
    };

    const onDecrement = () => {
        setContext('count', (count) => count - 1);
    };

    return {
        onAddition,
        onDecrement
    };
}

export default connect(useStateToProps)(Controls);

Please note: Multiple setContext calls will be batched based on React.setState batching whilst having only one render phase. Still you can use setContext.batchUpdates in order to queue multiple updates like this:

setContext.batchUpdates([
    ['firstPath', 'x'],
    ['secondPath', 'y']
]);

We've another hook that can perform batch updates on multiple stores as well and it's called useWisteriaBatchUpdater and we can use it as so:

import { useWisteriaBatchUpdater } from 'react-wisteria';

const useStateToProps = () => {
    const batchUpdater = useWisteriaBatchUpdater();

    const onClick = () => {
        batchUpdater([
            ['first-store', 'count', 100],
            ['second-store', 'count', 200]
        ]);
    };

    return {
        onClick
    };
}

Also, setContext is using golden-path syntax under the hood which means that you can update nested paths and arrays easily.

It's also important to remember to wrap dynamic data in the v function from golden-path in case it might have special tokens that might break the Golden Path parser.

import { v } from 'golden-path';

setContext('name', 'newName');
setContext(`peoples.0.id`, 12);
setContext(`peoples[id=1].name`, 'person name with id=1');
setContext(`peoples[name="Alex"]`, { name: 'Alex2' });

// For dynamic data it's recommended to wrap with v() in order to not break the parser.
// Same are relevant to decimal point as well (e.g. 2.2)
const nameFromServer = '[]&4%45.';
setContext(`peoples[name="${v(nameFromServer)}"].age`, 20);

// Greedy path to update all males
setContext(`peoples*[sex="male"].isMale`, true);

// Multiple condition - Not greedy (first match)
setContext(`peoples[id=1][age>=20]`, {});

// Multiple condition - Greedy (All matches)
setContext(`peoples*[id=1][age>=20].kind`, true);

Up until now, we can see that the Context we passed into the options of the library has wrapped the values of the Root props and we can inspect the state by extracting { context } and update it by extracting { setContext }.

(We can even compare objects by referential equality (===) since updates break the reference in the changed object up to the upper parent reference, so we can distinguish changes in each level without having to do expensive diffing.)

effects Option

There are two other major principles of react-wisteria - the handling of effects and derived states.

Let's say that we have an effect (General browsers API related stuff) that pops an alert message (or triggers a service request) if a specific condition is met (e.g. once the counter hits 10). In order to do this, we need access to context and setContext in our effects which allows us to inspect and respond with updates:

import React from 'react';

const useRequestReportOnTen = () => {
    const myStore = useWisteriaStore('my-store');
    const count = useWisteriaStateSlice(myStore, 'count');

    React.useEffect(() => {
        if (count !== 10) { return; }

        alert('Got ten and sending to the backend!');
    }, [count]);
};

export default useRequestReportOnTen;

First we define our hook - then we inject it into our options:

import { StoreProvider, useCreateStores } from 'react-wisteria';

const Counter = (props) => {
    const stores = useCreateStores([{ name: 'my-store', initialState: props, effects: [useRequestReportOnTen] }]);

    return (
        <StoreProvider stores={stores}>
            <div className="counter">
                <Display/>
                <Controls/>
            </div>
        </StoreProvider>
    );
};

export default Counter;

debugging

You can debug and trace your state updates by adding to the url the ?debugWisteria query param, or by setting window.isWisteriaDebugModeForced to true, You can also inspect your stores state last value by using window.ReactWisteriaStores and update them by using window.ReactWisteriaStoresUpdaters.

Doing ?debugWisteria=changed will console only real updates with different value than the current one, with only ?debugWisteria you might see unchanged updates and this might spam your console.

image

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].