Poznajemy patent na funkcję reducer w TypeScript, który przyda nam się do pisania reducerów w React + TS. Zaczynajmy.

Ok, zaczynamy od utworzenia obiektu initState:

const initState = {count: 0};

Ważna uwaga – ten obiekt nie będzie typowany. To z niego za pomocą typeof utworzymy sobie typ. Dzięki temu kiedyś zmienimy sobie initState, dodamy nowe pole, i reszta robi się za nas.

Ok, tworzymy enum z typami akcji:

const enum REDUCER_ACTION_TYPE {
    INCREMENT,
    DECREMENT
};

Ok, tworzymy typ akcji:

type ReducerAction = {
    type: REDUCER_ACTION_TYPE
};

Teraz funkcja reducer, a raczej jej sygnatura:

const reducer = (state: typeof initState, action: ReducerAction): typeof initState => {
    //(...)
}

Przyjmuje obiekt initState, typ jest wnioskowany po typeof. Przyjmuje też obiekt action, on jest również otypowany, może mieć pole type tylko takie, jak ma enum, który sobie napisaliśmy.

Funkcja zwraca ten sam typ, co initState, operator typeof nam wnioskuje, wystarczy potem zmienić coś w initState i reszta za tym nadąży.

Ok, teraz co dalej? Cóż, swich na action.type:

const reducer = (state: typeof initState, action: ReducerAction): typeof initState => {
    switch (action.type) {
        case REDUCER_ACTION_TYPE.INCREMENT:
            //(...)
        case REDUCER_ACTION_TYPE.DECREMENT:
            //(...)
        default:
            throw new Error()
    }
}

Fajnie, potrafimy już to podzielić na różne akcje. A co mamy zwrócić? Cóż, ten sam typ, jaki ma initState. Pamiętamy też, że stan nie jest mutowalny, jest obiektem referencyjnym (bo to obiekt), więc musimy jakoś zachować stary stan dodając zmodyfikowaną wartość.

Ok, tak to wygląda:

const reducer = (state: typeof initState, action: ReducerAction): typeof initState => {
    switch (action.type) {
        case REDUCER_ACTION_TYPE.INCREMENT:
            return { ...state, count: state.count + 1 }
        case REDUCER_ACTION_TYPE.DECREMENT:
            return { ...state, count: state.count - 1 }
        default:
            throw new Error()
    }
}

I dzięki temu podejściu zachowujemy stary stan, ale nadpisujemy count. To jest dobry wzorzec, bo nasz stan może mieć więcej pól i je chcemy zachować.

Ok, niech to się w głowie ułoży, więcej TSa już niedługo!