Nonprofit Pizza Maker

    @calvear/react-redux

    1.2.0 • Public • Published

    React Redux

    React library for eases Redux initialization for React SPA application. Is preconfigured and included helpful libraries as redux-logger and reselect.

    Structure 📋

    ├── README.md
    ├── LICENCE.md
    ├── CHANGELOG.md
    ├── .vscode/ # vscode shared development config
    ├── src/
    │   ├── effects/ # extra saga effects
    │   ├── hooks/ # extra redux hooks
    │   ├── reselect/ # reselect export bypass
    │   ├── utils/ # action types utils
    │   ├── middleware.js # middleware loader
    │   ├── store.js # createStore
    │   └── index.js
    ├── package.json
    ├── jsconfig.js
    ├── .babelrc
    ├── .eslintrc.json
    └── .prettierrc.json
    • store.js: exports createStore wrapper function and StoreProvider.
    • middleware.js: initializes middleware, redux-logger.

    How To Use 💡

    Should be initialized with StoreProvider on App.jsx like:

    import { StoreProvider } from '@calvear/react-redux';
    import store from 'store';
    
    export default function App() {
        return (
            <StoreProvider store={store}>
                <h1>Welcome to My App!</h1>
            </RouterProvider>
        );
    }

    So, you can create your first reducer, for example in store folder:

    ├── ...
    ├── store/
    │   ├── sample/
    │   │   │   ├── sample.partition.js # contains actions types and partition/store states
    │   │   │   ├── sample.reducer.js # reducer
    │   │   │   └── sample.saga.js # saga middleware
    │   └── index.js # exports default store
    ├── App.jsx
    └── index.js

    Define your partition definition, containing partition key and actions types in sample.partition.js:

    export default {
        // partition key
        Key: 'SAMPLE',
    
        // action types
        Type: {
            EXEC: 'EXEC',
            COMMIT: 'COMMIT',
            ROLLBACK: 'ROLLBACK',
        },
    
        // partition states
        State: {
            PREPARING: 'PREPARING',
            EXECUTING: 'EXECUTING',
            READY: 'READY',
            FAILED: 'FAILED',
        },
    };

    Define your reducer in sample.reducer.js:

    import SamplePartition from './sample.partition';
    
    export default function SampleReducer(store = {}, action) {
        const { type, payload } = action;
    
        switch (type) {
            // executes the action.
            case SamplePartition.Type.EXEC:
                return {
                    ...store,
                    state: SamplePartition.State.EXECUTING,
                    data: payload,
                };
    
            // action is successful.
            case SamplePartition.Type.COMMIT:
                return {
                    ...store,
                    state: SamplePartition.State.READY,
                };
    
            // action was finished with errors.
            case SamplePartition.Type.ROLLBACK:
                return {
                    ...store,
                    state: SamplePartition.State.FAILED,
                    error: payload,
                };
    
            // default doesn't changes the store,
            // so, components won't re-renders.
            default:
                return store;
        }
    }

    Finally (optional) your middleware saga in sample.saga.js:

    import { all, call, dispatch, takeLatest } from '@calvear/react-redux/effects';
    import SamplePartition from './sample.partition';
    import Service from 'adapters/service';
    
    function* exec({ payload }) {
        try {
            const data = yield call(Service.GetData);
    
            // Success action.
            yield dispatch(SamplePartition.Type.COMMIT, data);
        } catch (e) {
            yield dispatch(SamplePartition.Type.ROLLBACK, {
                stacktrace: e,
                message: 'Operation cannot be completed',
            });
        }
    }
    
    export default function* run() {
        yield all([
            // use all only if exists two or more listeners.
            takeLatest(SamplePartition.Type.EXEC, exec),
        ]);
    }

    Finally, your store/index.js file should looks like:

    import { createStore } from '@calvear/react-redux';
    import { SamplePartition, SampleReducer, SampleSaga } from './sample';
    
    const reducers = {
        [SamplePartition.Key]: SampleReducer,
    };
    
    const sagas = [SampleSaga()];
    
    export default createStore({ reducers, sagas, true });

    Hooks

    Library has custom hooks for eases partition handling.

    • usePartition: retrieves current partition state.
    import { usePartition } from '@calvear/react-redux/hooks';
    import { SamplePartition } from 'store/sample';
    
    export default function MainPage()
    {
        const { state, data, error } = usePartition(SamplePartition);
    
        ...
    }
    • useActionDispatch: returns an action dispatcher.
    import { useEffect } from 'react';
    import { useActionDispatch } from '@calvear/react-redux/hooks';
    import { SamplePartition } from 'store/sample';
    
    export default function MainPage()
    {
        const dispatchSampleExec = useActionDispatch(SamplePartition.Type.EXEC);
    
        useEffect(() =>
        {
            dispatchSampleExec({ someProp: 'hello world' });
        }, []);
    
        ...
    }

    Also, exports every hook from react-redux lib.

    • useSelector: extracts data from the Redux store state.
    import { useSelector } from '@calvear/react-redux/hooks';
    import { SamplePartition } from 'store/sample';
    
    export default function MainPage()
    {
        const state = useSelector(({ [SamplePartition.Key]: state }) => state);
        // in this example, will returns same that usePartition(...)
    
        ...
    }
    • useDispatch: returns a store dispatcher.
    import { useEffect } from 'react';
    import { useDispatch } from '@calvear/react-redux/hooks';
    import { SamplePartition } from 'store/sample';
    
    export default function MainPage()
    {
        const dispatch = useDispatch();
    
        useEffect(() =>
        {
            dispatch({
                type: SamplePartition.Type.EXEC,
                payload: { someProp: 'hello world' }
            });
            // in this example, will behaves like useActionDispatch(...)
        }, []);
        ...
    }
    • useStore: returns a reference to the same Redux store.
    import { useStore } from '@calvear/react-redux/hooks';
    
    export default function MainPage()
    {
        const store = useStore();
    
        const state = store.getState();
    
        ...
    }

    reselect

    Library integrates and exports reselect lib.

    • createSelector: creates a memoized selector.
    import { createPartitionSelector } from '@calvear/react-redux';
    import { useSelector } from '@calvear/react-redux/hooks';
    import { createSelector } from '@calvear/react-redux/reselect';
    import { SamplePartition } from 'store/sample';
    
    const sampleSelector = createPartitionSelector(SamplePartition);
    
    const sampleDataSelector = createSelector(
        sampleSelector,
        sample => sample.data
    )
    
    export default function MainPage()
    {
        const data = useSelector(sampleDataSelector);
    
        ...
    }

    Saga Effects

    Library has custom redux-saga effects.

    • dispatch: dispatches an action with optional payload.
    import { dispatch } from '@calvear/react-redux/effects';
    import { SamplePartition } from 'store/sample';
    
    function* exec({ payload })
    {
        // dispatches COMMIT action
        yield dispatch(
            SamplePartition.Type.COMMIT,
            payload
        );
    }
    
    ...
    • selectPartition: extracts partition from Redux state.
    import { selectPartition } from '@calvear/react-redux/effects';
    import { SamplePartition } from 'store/sample';
    
    function* exec()
    {
        // extracts sample state from store
        const { state, data, error } = yield selectPartition(SamplePartition);
    }
    
    ...
    • takeAny: waits for any action type to occur n times.
    import { takeAny } from '@calvear/react-redux/effects';
    import { SamplePartition } from 'store/sample';
    
    function* exec()
    {
        // waits for any EXEC or COMMIT action,
        // intercepting two of these dispatches
        const [
            firstResult,
            secondResult
        ] = yield takeAny([
            SamplePartition.Type.EXEC,
            SamplePartition.Type.COMMIT
        ], 2);
    }
    
    ...

    Avoid actions types collision

    Redux doesn't handles action types collision for reducers, so, for example if we has two partitions/reducers with same actions (EXEC, COMMIT), will conflicts dispatching any of these.

    This library provides of a utility for prefix partition key to every action type.

    import { packagePartitionHandler } from '@calvear/react-redux/utils';
    
    let SamplePartition = {
        // partition key
        Key: 'SAMPLE',
    
        // action types
        Type: {
            EXEC: 'EXEC',
            COMMIT: 'COMMIT',
            ROLLBACK: 'ROLLBACK',
        },
    
        ...
    };
    
    // prefixes action types with partition key
    export default packagePartitionHandler(SamplePartition);

    Linting 🧿

    Project uses ESLint, for code formatting and code styling normalizing.

    • eslint: JavaScript and React linter with Airbnb React base config and some other additions.
    • prettier: optional Prettier config.

    For correct interpretation of linters, is recommended to use Visual Studio Code as IDE and install the plugins in .vscode folder at 'extensions.json', as well as use the config provided in 'settings.json'

    Changelog 📄

    For last changes see CHANGELOG.md file for details.

    Built with 🛠️

    • React - the most fabulous JavaScript framework.
    • Redux - most popular frontend state handler.
    • React Redux - perfect React Redux integration.
    • Redux Saga - powerfull Redux middleware.
    • Redux Logger - impressive logger for Redux actions.
    • reselect - redux memoized selectors library.

    License 📄

    This project is licensed under the MIT License - see LICENSE.md file for details.


    by Alvear Candia, Cristopher Alejandro

    
    

    Install

    npm i @calvear/react-redux

    DownloadsWeekly Downloads

    0

    Version

    1.2.0

    License

    MIT

    Unpacked Size

    56.3 kB

    Total Files

    28

    Last publish

    Collaborators

    • calvear