DEV Community

Tetragius
Tetragius

Posted on • Edited on

State management library for react. Based on Proxy API.

I created it for myself but maybe someone will like it :)


DEMO

Motivation

I wanted to work with the state as with a regular object, not caring about anything. I did not want to write actions and reducers as in redux. Wanted something simple. Hide complex logic.

Meet Raxy

Github

Raxy means react plus proxy (JS Proxy API).
And it hides behind it all the work with the state. Giving you a proxied object

Raxy weighs approximately 2kb or 5kb with a polyfill for IE.

Instalation and usage

Instalation

its simple :)

npm install --save raxy

Then connect it in the project

import Raxy from 'raxy'; // with polyfill
import Raxy from 'raxy/next'; // without polyfill

Next, create a storage, in the example it is a simple object, but it can be with deep nesting. Raxy also proxies arrays (but more on that later)

const ToDo = {
    list: [
        {title: 'item 1', finished: false},
        {title: 'item 2', finished: false},
        {title: 'item 3', finished: false},
    ]
}
const {state, subscribe, connect} = new Raxy({ToDo});

state — proxied storage

Then you have two mechanisms for listening to changes. The first is the connect method. It takes as its arguments the react component and the storage mapping function into the component state.

export ConnectedComponent = connect(Component, store => ({list: store.list}));

Connect — returns the wrapped component

The second mechanism is the subscribe method; it takes callbacks as arguments, which returns an altered state. And the mapping function is similar to the connect method.

const subscriber = subscribe((state) => console.log(state), s => ({...s}));

Subscribe — returns an object with two methods: on and off

off — to suspend a subscription

on — to renew your subscription

If necessary, you can create a react hook

function useRaxy(mapper) {
    const [data, setState] = useState(mapper(state));

    useEffect(() => {
        let subscriber = subscribe(s => setState(s), mapper);
        return () => { // dont forget unsubscribe when dismount
            subscriber.off();
            subscriber = null;
        }
    });

    return data;
}
Usage

Now you can simply update the proxy object as an ordinary object.

state.list = state.list.map(i => {
            if(i === this.props.item){
                i.finished = !i.finished;
            }
            return i;
        });

or so, since arrays are proxied too

state.list[1] = {title: 'item 1', finished: true};
state.list[1].finished = false;

or even so

state.nested.a = 'new value';

if you need a kind of action from redux

const action = (c, e) => {
    const state = Object.assign({}, store.state);
    state.nested.c = c;
    state.nestedAgain.e = e;
    Object.assign(store.state, state);
}

action(4, 5);

You can also use history and redux-dev-tools

const history = createHashHistory({ basename: '/' });

const {state, subscribe} = new Raxy({
    list: [
        { label: 'item 1', status: false },
        { label: 'item 2', status: false },
        { label: 'item 3', status: true },
    ],
    /*!!*/ location: history.location, /*!!*/
    nested: { b: 2 }
});

state.subscribe(location => console.log(location), state => ({ location: state.location }));

history.listen(location => state.location = location);
const {state, subscribe} = new Raxy({});

const devTools = window.__REDUX_DEVTOOLS_EXTENSION__.connect();

devTools.init({ value: state });

subscribe(state => devTools.send('change state', { value: state }), state => ({ ...state }));

How it works

source file

Raxy — wraps the repository object recursively by calling the proxy method on all nested objects or arrays. Only the ‘set’ method is proxied.

By calling subscribe or connect a new subscriber is added to the stack.
To prevent a memory leak, you can unsubscribe from updates and remove proxy objects using the revoke method.

Inside the interception function of the ‘set’ method, a comparison is made between the memorized previous state and the new one. (The state is returned by the mapping function of the dedicated in subscribe or connect)

If the state is new, then the callback method for subscribe is called, or the setState method for the wrapped component.

For a component, if it is unmounted, an automatic cancellation of the state updates occurs.

If you like more information, you can refer to -> Github

Top comments (0)