Kontynuacja lekcji poprzedniej, funkcja reducer w TypeScript, przyda się nam to do Reacta. Tym razem ogarniamy opcjonaly payload. Do dzieła!

Ok, przypomnijmy sobie poprzednią funkcję reducer:

const initState = {count: 0};

const enum REDUCER_ACTION_TYPE {
    INCREMENT,
    DECREMENT
};

type ReducerAction = {
    type: REDUCER_ACTION_TYPE
};

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()
    }
}

Ok, teraz nasz initState będzie nieco bardziej zaawansowany:

const initState = { count: 0, text: '' }

Enum na typy akcji:

const enum REDUCER_ACTION_TYPE {
    INCREMENT,
    DECREMENT,
    NEW_INPUT,
}

Typ akcji, z opcjonalnym payloadem określonego typu:

type ReducerAction = {
    type: REDUCER_ACTION_TYPE,
    payload?: string,
}

Piszemy sygnaturę reducera:

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

Jak wszystko jasne, to kontynuujemy:

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 }
        case REDUCER_ACTION_TYPE.NEW_INPUT:
            //(...)
        default:
            throw new Error()
    }
}

Teraz, gdy nasz stan ma inne pola niż tylko count, widzimy po co nam ten spread. Bo mutować stanu nie możemy, musimy zwrócić nowy stan. Ale nie wszystkie pola chcemy nadpisać, w przypadku akcji powyżej tylko count ma być nadpisany (o wartość -1 lub +1).

Ok, pomyślmy jak załatwić new input. Już się chyba domyślamy, że trzeba go zastąpić payloadem:

 case REDUCER_ACTION_TYPE.NEW_INPUT:
            return { ...state, text: action.payload  }
            //Type 'string | undefined' is not assignable to type 'string'.

No właśnie, payload może być opcjonalny. Zaś obiekt musi mieć string pod kluczem text. Jak to rozwiązać? Dość łatwo:

case REDUCER_ACTION_TYPE.NEW_INPUT:
            return { ...state, text: action.payload ?? "" }
            //ok

Te operatory już powinniśmy znać na tym poziomie. Cała funkcja:

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 }
        case REDUCER_ACTION_TYPE.NEW_INPUT:
            return { ...state, text: action.payload ?? "" }
            //ok
        default:
            throw new Error()
    }
}

Ok, jeżeli to ogarniamy, to w następnej lekcji połączymy to z Reactem. Do następnego razu!