4
votes

I intend to create a simple counter using react redux in typescript.

I have defined my store with actions and reducers in the following manner but not sure how to invoke dispatch with a specific action

import * as React from 'react';
import { createStore, Action, Reducer } from 'redux';

export interface CounterState {
    counter: number;
}

export enum ActionTypes {
    INCREMENT = 'increment',
    DECREMENT = 'decrement'
}

export interface IncAction { type: ActionTypes.INCREMENT }
export interface DecAction { type: ActionTypes.DECREMENT }

export type CounterAction = IncAction | DecAction;

const reducer: Reducer<CounterState> = (state: CounterState = {counter: 0}, action: CounterAction) => {
    switch (action.type) {
        case ActionTypes.INCREMENT:
            return { ...state, counter: state.counter + 1};
        case ActionTypes.DECREMENT:
            return { ...state, counter: state.counter - 1};
        default:
            return state;
    }
};

let store = createStore(reducer, { counter: 0 });

Following is how my react component Counter looks like

interface IProps {}

interface IState {}

export default class Counter extends React.Component<IProps, IState> {

private unsubscribe: Function;

constructor(props: IProps, context?: any) {
    super(props, context);
}

componentDidMount() {
    this.unsubscribe = store.subscribe(() => this.render());
}

componentWillUnmount() {
    this.unsubscribe();
}

render() {
    const { counter } = store.getState();
    return (
        <div>
            <p>
                <label>Counter: </label><b>#{counter}</b>
            </p>
            <button onClick={e => store.dispatch('increment') }>+</button>
            <span style={{ padding: "0 5px" }} />
            <button onClick={e => store.dispatch('decrement') }>-</button>
        </div>
    );
}

}

I am getting following error -

ERROR in [at-loader] ./src/components/Counter.tsx:63:54 TS2345: Argument of type '"increment"' is not assignable to parameter of type 'AnyAction'.

ERROR in [at-loader] ./src/components/Counter.tsx:65:54 TS2345: Argument of type '"decrement"' is not assignable to parameter of type 'AnyAction'.

2

2 Answers

2
votes

Look at the actual type definition of Action and AnyAction:

export interface Action {
  type: any;
}

export interface AnyAction extends Action {
  // Allows any extra properties to be defined in an action.
  [extraProps: string]: any;
}

It needs to be an object and it must have a type property NOT just a string.

You need an action creator that returns at a minimum an object with a type property. You can also pass this object directly, which is what I assume you where attempting to do:

store.dispatch({type: ActionTypes.INCREMENT})

I would also recommend using the connect HOC to connect state to your component since doing const { counter } = store.getState(); will not trigger a re-render when the counter value in your store changes. If you want a more basic example:

...
  this.unsubscribe : () => void

  componentDidMount() {
    this.unsubscribe = store.subscribe(() => this.setState({ store.getState() }))
  }

  componentWillUnmount() {
    this.unsubscribe()
  }
...

Then reference the component's local state in render via const { counter } = this.state;

1
votes

I "fixed" this by doing this:

const _ = (state: any = 0, _: AnyAction) => state;
const root = combineReducers({
    _,
    applicationStatusReducer,
    ...
});

It seems to fix the issue if you just add an empty reducer with the action type of "AnyAction" to it.

Obviously this isn't actually fixing the underlying issue, but for people who are content with the result of the fix above, this is a dirty way to do it.

Use at your own discretion.