среда, 14 июня 2017 г.

TypeScript Redux Types

import * as React from 'react';
import * as ReactDOM from 'react-dom';

import {createStore, combineReducers, applyMiddleware, Dispatch, Reducer} from 'redux';
import {Provider, connect} from 'react-redux';

import * as logger from 'redux-logger';
import thunk from 'redux-thunk';

// Action types

type TCounterUp = 'COUNTER_UP';
type TCounterDown = 'COUNTER_DOWN';

const COUNTER_UP: TCounterUp = 'COUNTER_UP';
const COUNTER_DOWN: TCounterDown = 'COUNTER_DOWN';

// Actions

type TCouterUpAction = {
    type: TCounterUp,
    value: number
};

type TCouterDownAction = {
    type: TCounterDown,
    value: number
};

type TCounterAction = TCouterUpAction
                                 | TCouterDownAction;

const couterUpAction = (value: number): TCouterUpAction => {
    return {
        type: COUNTER_UP,
        value: value
    };
};

const couterDownAction = (value: number): TCouterDownAction => {
    return {
        type: COUNTER_DOWN,
        value: value
    };
};

const couterDownAsync = (value: number) => {
    return function (dispatch: Dispatch<any>): void {
       setTimeout((): void => {
           dispatch(couterDownAction(value));
       }, 1000);
    };
};

// Reducers

interface ICounterState {
    value: number;
}

const defaultCounterState: ICounterState = {value: 0};

const counterReducer = (state: ICounterState = defaultCounterState, action: TCounterAction): ICounterState => {
    switch (action.type) {
        case COUNTER_UP: return Object.assign({}, state, {value: state.value + action.value});
        case COUNTER_DOWN: return Object.assign({}, state, {value: state.value - action.value});
        default: return state;
    }
};

const outterReducer: Reducer<any> = combineReducers({
    inner: counterReducer
});

const rootReducer: Reducer<any> = combineReducers({
    counter: outterReducer
});

// Store

const initialStoreState: Object = {
    counter: {
        inner: {
            value: 0
        }
    }
};

const store = createStore(rootReducer, initialStoreState, applyMiddleware((logger as any).createLogger(), thunk));

// Interfaces

interface IStateProps {
    counter: number;
}

interface IDispatchProps {
    handleUpClick: () => void;
    handleDownClick: () => void;
}

interface IOwnProps {
    myProp: number;
}

type IProps = IStateProps & IDispatchProps & IOwnProps;

interface IState {}

// Component

class MyComponent extends React.Component<IProps, IStateProps> {
    constructor (props) {
        super(props);
    }
    render (): JSX.Element {
        const {
            myProp,
            counter,
            handleUpClick,
            handleDownClick
        } = this.props;
        return (
            <div>
                <div>Моё свойство: {myProp}</div>
                <span>{counter}</span>{' '}
                <span onClick={handleUpClick}>Вверх</span>{' '}
                <span onClick={handleDownClick}>Вниз</span>
            </div>
        );
    }
}

// Connect

const mapStateToProps = (state: any, ownProps: IOwnProps): IStateProps => {
    return {
        counter: state.counter.inner.value
    };
};

const mapDispatchToProps = (dispatch: Dispatch<any>, ownProps: IOwnProps): IDispatchProps => {
    return {
        handleUpClick: (): void => {
            dispatch(couterUpAction(1));
        },
        handleDownClick: (): void => {
            dispatch(couterDownAsync(1));
        }
    };
};

const ConnectedMyComponent = connect<IStateProps, IDispatchProps, IOwnProps>(mapStateToProps, mapDispatchToProps)(MyComponent);

interface IRootComponentProps {}

interface IRootComponentState {}

class RootComponent extends React.Component<IRootComponentProps, IRootComponentState> {
    render (): JSX.Element {
        return (
            <ConnectedMyComponent myProp={123} />
        );
    }
}

ReactDOM.render(<Provider store={store}><RootComponent /></Provider>, document.getElementById('root'));

Комментариев нет:

Отправить комментарий