dojo / interop

Provides bindings and interoperability between Dojo packages and other libraries
Other
1 stars 14 forks source link

Redux Injector (Enables bindings between redux and dojo 2 widget-core) #2

Closed agubler closed 7 years ago

agubler commented 7 years ago

Type: feature

The following has been addressed in the PR:

Description:

Adds a redux injector that can used to bind a redux store and dojo 2 widgets using the Container pattern.

Define an injector in the registry and then use Container to inject the store.

Example Usage:

import { WidgetBase } from '@dojo/widget-core/WidgetBase';
import { registry, v } from '@dojo/widget-core/d';
import { Injector } from '@dojo/widget-core/Injector';
import { Container } from '@dojo/widget-core/Container';
import { ProjectorMixin } from '@dojo/widget-core/mixins/Projector';
import { ReduxInjector } from '@dojo/interop/redux';
import { createStore } from 'redux';

// the reducer for the example state
function reducer(state: any = { counter: 0 }, { type, payload }: any) {
    switch (type) {
        case 'INCREMENT_COUNTER':
            return { counter: state.counter + 1 };
        case 'DECREMENT_COUNTER':
            return { counter: state.counter - 1 };
        default:
            return state;
    }
}

// create the redux store with reducers
const store = createStore(reducer);

// Defines the injector in the registry
registry.define('redux-store', Injector(ReduxInjector, store));

// properties for the example widget
interface ReduxExampleProperties {
    counter: number;
    incrementCounter: () => void;
    decrementCounter: () => void;
}

// example widget
class ReduxExample extends WidgetBase<ReduxExampleProperties> {
    decrement() {
        this.properties.decrementCounter();
    }

    increment() {
        this.properties.incrementCounter();
    }

    render() {
        return v('span', [
            v('button', { onclick: this.decrement, disabled: this.properties.counter === 0 }, [ 'decrement' ]),
            v('span', [ ` Counter ${this.properties.counter} ` ]),
            v('button', { onclick: this.increment }, [ 'increment' ])
        ]);
    }
}

// binding the redux injector and the widget together, specifying a function that injects attributes
// from the store into the widget as properties
const ReduxExampleContainer = Container(ReduxExample, 'redux-store', { getProperties: (store: Store<any>, properties: any) => {
    const state = store.getState();

    function incrementCounter() {
        store.dispatch({ type: 'INCREMENT_COUNTER' });
    }

    function decrementCounter() {
        store.dispatch({ type: 'DECREMENT_COUNTER' });
    }
    return {
        counter: state.counter,
        incrementCounter,
        decrementCounter
    };
}});

// append the widget
const Projector = ProjectorMixin(ReduxExampleContainer);
const projector = new Projector();
projector.append();

Resolves #1

agubler commented 7 years ago

Needs tests

codecov[bot] commented 7 years ago

Codecov Report

:exclamation: No coverage uploaded for pull request base (master@24faa62). Click here to learn what that means. The diff coverage is 90%.

Impacted file tree graph

@@          Coverage Diff           @@
##             master    #2   +/-   ##
======================================
  Coverage          ?   90%           
======================================
  Files             ?     1           
  Lines             ?    10           
  Branches          ?     1           
======================================
  Hits              ?     9           
  Misses            ?     0           
  Partials          ?     1
Impacted Files Coverage Δ
src/redux/ReduxInjector.ts 90% <90%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 24faa62...4ba2526. Read the comment docs.