Skip to content
This repository has been archived by the owner on May 26, 2022. It is now read-only.

rscopes/react-scopes

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
src
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

react-scopes

Flexible State management system based on flux architecture, stores data components & inheritable scopes

Build Status

What's react-scopes ?

RS is a simple to use & flexible state management system inspired by ReactJS methods.
Quickly said, RS allow linking, scoping, and sequencing multiples async-able stores while remaining in a serializable flux architecture.

RS Stores look like "React components for data".
Basically, they render determined data according theirs current state & propagate these data to the listening stores state .

Scopes ?

At first, scopes are like stores that would compose their state & data basing on the active stores they contain.
But they were made for flexibility so they have 3 "scalability dimensions" :

  • They can contain hierarchically named scopes
  • They can have parent scope ( in the js prototypes way )
  • And they can have "mixed" scopes

Note

RS use a similar states data processing structure than React Hooks & Contexts;
Main difference is RS atomically manage memo/state triggering & results propagation where Hooks just re-execute the memoizers graph every renders.

That's said, Hooks are simplier & have ways to achieve almost the same behaviors; so even if RS is pretty stable RS dev is stopped.
*actually there only decorators style injecters

Minimal doc here

Samples here

Simplified & limited sample :

import React       from "react";
import RS, {asRef, Scope} from "react-scopes";

const appScope = new Scope({

        // Here a simple store definition ( only instantiated if used )
        @RS.store
        config: {
        	// simple props define the initial state
            apiUrl: "https://somewhere.com",
            
            // functions defines actions
            // ( action return mutations / updates for the store state )
            changeApiUrl:({apiUrl})=>({apiUrl}),
            
            // when the state of this store change 
            // the $apply function update or replace the store result data
            // This resulting data object is the "public" value of this store
            // It *should* be predictable basing the state object for good async SSR  
            $apply(data={}, state, changesInState){
            	data.apiUrl = state.apiUrl+"/api";
            	return data;
            }
        },
    });

// Any App instance will use the same "appScope" Scope instance 
@RS(appScope)
class App extends React.Component {
    render() {
    	// MyComp will inherit appScope
        return <MyComp active={true}/>
    }
}

// Here; RS will instantiate the following scope definition 
// with any instance of the "MyComp" React Component 
@RS(
    {
        @RS.store
        master: {
            go: false,
        },
        @RS.store
        test  : {
        	// @asRef allow defining "references" to any reachable store in the scope
        	// Here "config" exist in the parent scope ("appScope") 
            @asRef
            config: "config",
            
            @asRef
            activateQuery: "master.go",
            
            // the $apply fn can update the data object using both sync and async methods
            $apply( data = {}, state, { activateQuery } ) {
                if ( activateQuery ) {
                	// All stores can call this.wait() & this.release()
                	// Quickly said, this.wait(optionalTokenForDebug) 
                	// will make this store & parent scope "unstable"
                	// so the store will not push result data to the listening stores
                	// when this.release(optionalTokenForDebug) is called the result data of the store is propagated
                    this.wait();
                    setTimeout(
                        tm => {
                            this.push({ state: "stable", value: "#asyncData2" });
                            this.release();
                        }, 500
                    );
                    return ({ state: "querying", value: undefined });
                   
                }
                return data;
            }
        },
        @RS.store
        test2 : {
            @asRef
            config: "config",
            @asRef
            activateQuery: "master.go",
            
            // Another async method consist on updating the data object dynamically
            $apply( data = {}, state, { activateQuery } ) {
                if ( activateQuery ) {
                    this.wait(); 
                    setTimeout(
                        tm => {
                            data.state = "stable";
                            data.value = "#asyncData1";
                            this.release();
                        }, 550
                    );
                    data.state = "querying";
                    data.value = undefined;
                }
                return data;
            }
        }
    }
)
// bind this.props.active values to master.go
@RS.fromProps("active:master.go")
// bind test & test2 to the props ( & mount any required store ) 
@RS.connect("test", "test2")
class MyComp extends React.Component {
    render() {
        let { test, test2 } = this.props;
        return <div className={'target'}>{test.value}-{test2.value}</div>
    }
}

Notes

RS seems semantically & functionally stable.
It's free of memory leaks & have correct perfs.

That's said RS was not written trying to be the faster/cleaner system one shot, but to :

  • Have better scalability ( by making complex components independent )
  • Easily define & reuse async data process
  • Make async SSR
  • Avoid using tons of independents libs to manage the app state
  • Easily serialize & restore full or partial app state
  • etc

RS use a similar states data processing structure than React Hooks & Contexts;
Main difference is RS manage memo/state triggering & results propagation where Hooks juste re-execute the memoizers graph every renders.

About decorators

( well, like code-decorators )

These are stage-0 decorators because while the decorators spec has changed and is now stage-2, no transpiler has yet to implement these changes and until they do, this library won't either. Although the TypeScript documentation uses the phrase "Decorators are a stage 2 proposal for JavaScript" this is misleading because TypeScript still only implements the stage-0 version of the spec, which is very incompatible with stage-2 (as of this writing). If you concretely find that a compiler (babel, TS, etc) implement stage-2+, please do link me to the appropriate release notes! 🎈

This can be consumed by any transpiler that supports stage-0 of the decorators spec, like babel.js version 5. Babel 6 does not yet support decorators natively, but you can include babel-plugin-transform-decorators-legacy or use the applyDecorators() helper.

Related packages

rScopes link the following packages :

ReScope : rescope
ReScope HOCs, decorators & tools : rescope-spells

Buy Me A Coffee

*