Creating a reducer

In this section, we are going to implement a reducer, which is responsible for changing the state in the store for a given action. Let's carry out the following steps:

  1. We'll start by importing the Reducer type from Redux, along with the combineReducers function:
import { 
Action,
ActionCreator,
Dispatch,
Reducer,
combineReducers

} from 'redux';

  1. Let's create the skeleton reducer function:
const questionsReducer: Reducer<QuestionsState, QuestionsActions> = (
state = initialQuestionState,
action
) => {
// TODO - Handle the different actions and return new state
return state;
};

The reducer takes in two parameters: one for the current state and another for the action that is being processed. The state will be undefined the first time the reducer is called, so we default this to the initial state we created earlier.

The reducer needs to return the new state object for the given action. We're simply returning the initial state at the moment.

We've used the Reducer generic type for the reducer, which takes in parameters for the state type and actions type. Notice that we use the QuestionsActions union type for the actions type.

  1. Let's add a switch statement to handle the different actions:
const questionsReducer: Reducer<QuestionsState, QuestionsActions> = (
state = initialQuestionState,
action
) => {
switch (action.type) {
case 'GettingUnansweredQuestions': {
// TODO - return new state
}
case 'GotUnansweredQuestions': {
// TODO - return new state
}
case 'PostedQuestion': {
// TODO - return new state
}
}
return state;
};

Notice that the type property in the action parameter is strongly-typed and that we can only handle the three actions we defined earlier.

  1. Let's handle the GettingUnansweredQuestions question first:
switch (action.type) {
case 'GettingUnansweredQuestions': {
return {
...state,
unanswered: null,
loading: true
};
}
case 'GotUnansweredQuestions': {
// TODO - return new state
}
case 'PostedQuestion': {
// TODO - return new state
}
}

We use the spread syntax to copy the previous state into a new object, initialize the unanswered state to null, and set the loading state to true.

  1. Let's move on to the GotUnansweredQuestions action:
switch (action.type) {
case 'GettingUnansweredQuestions': { ... };
case 'GotUnansweredQuestions': {
return {
...state,
unanswered: action.questions,
loading: false
};
}
case 'PostedQuestion': {
// TODO - return new state
}
}

Again, we use the spread syntax to copy the previous state into a new object and set the unanswered and loading properties. Notice how we get IntelliSense only for the properties in the GotUnansweredQuestions action:

TypeScript has smartly narrowed down the type in the switch branch from the union type that was passed into the reducer for the action parameter. 

  1. Let's handle the last action:
switch (action.type) {
case 'GettingUnansweredQuestions': { ... };
case 'GotUnansweredQuestions': { ... };
case 'PostedQuestion': {
return {
...state,
unanswered: action.result
? (state.unanswered || []).concat(action.result)
: state.unanswered,
postedResult: action.result
};
}
}

We create a new state from the old state using the spread syntax. If the question has been successfully submitted, the result property in the action will contain a question property, which is added to the unanswered array using the array's concat function. We store the result of the question submission in the postedResult property.

  1. We've handled all the different actions now but are going to do a little more work that will help us remember to handle new actions in the reducer as our app grows. To do this, we are going to add a default branch in the switch statement:
switch (action.type) {
case 'GettingUnansweredQuestions': { ... };
case 'GotUnansweredQuestions': { ... };
case 'PostedQuestion': { ... };
default:
neverReached(action);
}

This will inform the TypeScript compiler that the default branch should never be reached. So, if we add a new action and it isn't handled in the reducer, the default branch will be reached and a compiler error will be raised.

  1. Let's implement the neverReached function we just referenced below the reducer:
const neverReached = (never: never) => {};

The function takes in a parameter that is of the never type and returns an empty object. 

The never type is a TypeScript type that represents something that would never occur and is typically used to specify unreachable areas of code.

So, if TypeScript can reach this function and the never parameter, it will throw an error.

  1. Now, we are going to use the combineReducers function in Redux to combine all our reducers into a single reducer that returns AppState:
const rootReducer = combineReducers<AppState>({
questions: questionsReducer
});

An object literal is passed into combineReducers that contains the properties in our app state, along with the reducer that is responsible for that state. We only have a single property in our app state called questions and a single reducer managing changes to that state called questionsReducer

We will use the rootReducer in the next section when we create the store object.

That's the reducers complete. Now, we have all the different pieces implemented for our Redux store, so we are going to create a function to create the store in the next section. 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.38.142