1.3.6 • Published 6 years ago

raxy v1.3.6

Weekly downloads
3
License
MIT
Repository
github
Last release
6 years ago

logo

Build Status npm version License: MIT

Raxy (ReAct + ProXY)

Simple react state manager. You can work with state as with a regular object. Can be used with redux-devtools-extension and history. Also works with react hooks.

Powered by Proxy API. It is possible to dynamically create wrappers (page-two in the example) for rendering optimization or using react hooks.

~2kb or ~5kb with plyfill for IE

DEMO

const initalState = {any: 1};
const {state, connect, subscribe} = Raxy<IState>(initalState); 

/* IState */ state.any = 5; // update state;

connect<IComponentProps>(Component: ComponentClass, mapper(state: IState):IComponentProps)

subscribe<IProps>(callback(props: IProps), mapper(state: IState):IProps)

Based on JS Proxy API and works with all browsers that support it. Also IE 10+ because has polyfill.

ChromeFirefoxIEOperaSafari
49+ ✔18+ ✔IE 10+ (Edge)18+ ✔36+ ✔10+ ✔

Navigation

Installation

npm install --save raxy

API description

Create a new store call:

new Raxy<IState>({initialState}, [callback]);

this returns two methods and proxied state

Methods

connect the react component to the store:

connect<IComponentProps>(Component, mapper) : WrappedComponent;

component - react component

mapper - map store to component props (return Partial)


connect listener to the store:

subscribe<T>(callback, mapper) : ISubscriber;

callback - function with an argument containing the value returned by the mapper

mapper - map store for callback (return T)

retrun object with off and on methods;

Poxied state

state
// you can chage this just chage object properties

Simple usage

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

Create store

// store.js
import Raxy from 'raxy';

// initial app state

const ToDo = {
    list: [
        {title: 'item 1', finished: false},
        {title: 'item 2', finished: false},
        {title: 'item 3', finished: false},
    ]
}

// create new store
export const {state, connect} = new Raxy(ToDo);

Usage in react

// component.jsx
import React from 'react';
import { connect, state } from './store';

class Component extends React.Component {
    constructor(props){
        super(props);
    }

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

    render(){

        const list = this.props.list;

        return (
            <div>
                {
                    list && list.map((item, idx) => {
                        return <div key={idx} onClick={() => this.click(item)}>
                            {item.title} - {JSON.stringify(item.finished)}
                        </div>
                    });
                }
            </div>);
    }
}

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

Or decorator style

import * as React from 'react';
import { componentDecorator } from '../store';
import ListDynamic from './listDynamic';

@componentDecorator(store => ({ list: store.listB, pathName: store.location.pathname, listCount: store.listB.length }))
export default class PageDynamic extends React.PureComponent<any, any> {
    render() {
        return <div className={`page ${this.props.pathName.split('/')[1]}`}>
            <div>List B</div>
            <ListDynamic items={this.props.list} l={this.props.listCount} />
        </div>
    }
}

Additional

Store can be more complicated

const ToDo = {
    list: [
        {title: 'item 1', finished: false},
        {title: 'item 2', finished: false},
        {title: 'item 3', finished: false},
    ]
}

const AnotherToDo = {
    list: [
        {title: 'item 1', finished: false},
        {title: 'item 2', finished: false},
        {title: 'item 3', finished: false},
    ]
}

const Another = {
    ObjectA: {
        a: 1,
        b: 2
    },
    ObjectB: {
        a: 1,
        b: 2
    }
}

// create new store
export const {state, connect} = new Raxy({ToDo, AnotherToDo, Another});

Also you can subscribe or update item in arrays

const ToDo = {
    list: [
        {title: 'item 1', finished: false},
        {title: 'item 2', finished: false},
        {title: 'item 3', finished: false},
    ]

export const {state, subscribe} = new Raxy({ToDo});

subscribe((s) => console.log(s), state => ({ list: state.list }));
subscribe((s) => console.log(s), state => ({ item: state.list[1] }));

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

Updates & side-effects

    // you can update store like
    state.Another.ObjectA.a = 3;
    state.Another.ObjectA = {
        a: 3,
        b: 4,
        c: 5
    }
    state.ToDo.list = [...state.ToDo.list, {title: 'item 4', finished: false}];

    // you can connect like
    connect(Component, store => ({list: store.ToDo.list}));
    connect(Component, store => ({countFinished: store.ToDo.list.filter(i => i.finished).length}));
    connect(Component, store => ({b: store.Another.ObjectA.b}));

Dynamic connect (from example)

// example/src/pageDynamic.tsx

import * as React from 'react';
import { connect } from '../store';
import ListDynamic from './listDynamic';

class PageDynamicComponent extends React.Component<any, any> {
    render() {
        return <div className={`page ${this.props.pathName.split('/')[1]}`}>
            <div>List B</div>
            <ListDynamic items={this.props.list} />
        </div>
    }
}

const PageDynamic = connect<any>(PageDynamicComponent, store => ({ list: store.listB, pathName: store.location.pathname }));
export default PageDynamic;
// example/src/listDynamic.tsx
import * as React from 'react';
import { connect } from '../store';
import ListItem, { IListItemProps } from './listItem';

export default class ListDynamic extends React.Component<any, any> {

    click = (idx, item) => {
        // you can update props because it is Proxy or you can import 'state' from '../store';
        this.props.items[idx] = { label: item.label, finished: !item.finished };
    }

    defineListItem = idx => {
        // you can create connection dynamicly;
        return connect<IListItemProps>(ListItem, s => ({ item: s.listB[idx] }));
    }

    render() {
        return (
            <div className='list'>
                {this.props.items.map((item, idx) => {
                    const Item = this.defineListItem(idx);
                    return <Item key={idx} onClick={() => this.click(idx, item)} />;
                })}
            </div>
        )
    }
}

Actions (optional)

You can create actions for combine multiple operations at one.

const store = new Raxy({ 
    a: 1, b: 2, 
    nested: { c: 3, nested: { d: 4 } }, 
    nestedAgain: { e: 5 } });

store.subscribe((s) => expect(s).to.equal(3), state => ({ d:state.nested.nested.d }));

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);

React hooks

You can work with hooks (from example)

import { subscribe, state } from '../store';
import React, { useState, useEffect } from 'react';

// create custom 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;
}

export function Hook() {
    const data = useRaxy(s => ({ val: 'nested is: ' + s.nested.itemOne }));

    return <div className='counter'>
        <div>{data.val} (Functional component with hook)</div>
    </div>
}

Subscribes

const {state, connect, /*!!*/ subscribe /*!!*/} = new Raxy({ToDo, AnotherToDo, Another});

subscribe((state) => console.log(state), s => ({...s})); // example

// or
// const subscriber = subscribe((state) => console.log(state), s => ({...s}));
// subscriber.off() - stop listen;
// subscriber.on() - start again;

Dev-tools & history examples

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 devTools = (window as any).__REDUX_DEVTOOLS_EXTENSION__ && (window as any).__REDUX_DEVTOOLS_EXTENSION__.connect();
const callback = store => devTools && devTools.send('change state', { value: { ...store } });

export const { state, connect, subscribe } = new Raxy(initialState, callback);

devTools && devTools.init({ value: state });
1.3.6

6 years ago

1.3.5

6 years ago

1.3.4

6 years ago

1.3.3

6 years ago

1.3.2

6 years ago

1.3.1

6 years ago

1.3.0

6 years ago

1.2.9

6 years ago

1.2.8

6 years ago

1.2.7

6 years ago

1.2.6

6 years ago

1.2.5

6 years ago

1.2.4

6 years ago

1.2.3

6 years ago

1.2.2

6 years ago

1.2.1

6 years ago

1.2.0

6 years ago

1.1.15

6 years ago

1.1.14

6 years ago

1.1.13

6 years ago

1.1.12

6 years ago

1.1.11

6 years ago

1.1.10

6 years ago

1.1.9

6 years ago

1.1.8

6 years ago

1.1.7

6 years ago

1.1.6

6 years ago

1.1.5

6 years ago

1.1.4

6 years ago

1.1.2

6 years ago

1.1.1

6 years ago

1.0.9

6 years ago

1.0.8

6 years ago

1.0.7

6 years ago

1.0.6

6 years ago

1.0.5

6 years ago

1.0.4

6 years ago

1.0.3

6 years ago

1.0.2

6 years ago

1.0.1

6 years ago

1.0.0

6 years ago