55

Strongly-typed React Redux Code with TypeScript

 5 years ago
source link: https://www.tuicool.com/articles/hit/RZVzMnY
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Redux is a popular library used to manage state in React apps. How can we make our Redux code strongly-typed with TypeScript – particularly when we have asynchronous code in the mix? Let’s find out by going through an example of a store that manages a list of people …

bErYVjv.png!web

State

Let’s start with the stores state object:

So, our app just contains an array of people. We have flags in the state to indicate when people are being loaded from the server and when a new person is being posted to the server. We’ve declared the state as readonly so that we don’t accidentally directly mutate the state in our code.

Actions

A change to state is initiated by an action. We have 4 actions in our example:

  • GettingPeople . This is triggered when a request is made to get the array of people from the server
  • GotPeople . This is triggered when the response has been received with the array of people from the server
  • PostingPerson . This is triggered when a request is made to the server to add a new person
  • PostedPerson . This is triggered when the response has been received from the server for the new person

Here’s the code:

So, our action types extend the generic Action type which is in the core Redux library, passing in the string literal that the type property should have. This ensures we set the type property correctly when consuming the actions in our code.

Notice the PeopleActions union type that references all 4 actions. We’ll later use this in the reducer to ensure we are interacting with the correct actions.

Action creators

Actions creators do what they say on the tin and we have 2 in our example. Our action creator is asynchronous in our example, so, we are using Redux Thunk . This is where the typing gets a little tricky …

The first action creator gets the people array from the server asynchronously dispatching 2 actions along the way:

ActionCreator is a generic type from the core Redux library that takes in the type to be returned from the action creator. Our action creator returns a function that will eventually return IGotPeopleAction . We use the generic ThunkAction from the Redux Thunk library for the type of the nested asynchronous function which has 4 parameters that have commented explanations.

The second action creator is similar but this time the asynchronous function that calls the server has a parameter:

So, the typing is fairly tricky and there may well be an easier way!

Reducers

The typing for the reducer is a little more straightforward but has some interesting bits:

We use the generic Reducer type from the core Redux library passing in our state type along with the PeopleActions union type.

The switch statement on the action type property is strongly-typed, so, if we mistype a value, a compilation error will be raised. The action argument within the branches within the switch statement has its type narrowed to the specific action that is relevant to the branch.

ma2yUju.png!web

Notice that we use the never type in the default switch branch to signal to the TypeScript compiler that it shouldn’t be possible to reach this branch. This is useful as our app grows and need to implement new actions because it will remind us to handle the new action in the reducer.

Store

Typing the store is straightforward. We use the generic Store type from the core Redux library passing in type of our app state which is IAppState in our example:

Connecting components

Moving on to connecting components now. Our example component is a function-based and uses the super cool useEffect hook to load the people array when the component has mounted:

The mapStateToProps function is straightforward and uses our IAppState type so that the references to the stores state is strongly-typed.

The mapDispatchToProps function is tricky and is more loosely-typed. The function takes in the dispatch function but in our example we are dispatching action creators that are asynchronous. So, we are using the generic ThunkDispatch type from the Redux Thunk core library which takes in 3 parameters for the asynchronous function result type, asynchronous function parameter type as well as the last action created type. However, we are using dispatch for 2 different action creators that have different types. This is why we pass the any type to ThunkDispatch and AnyAction for the action type.

Wrap up

We can make our redux code strongly-typed in a fairly straightforward manner. The way that TypeScript narrows the action type in reducers is really smart and the use of never is a nice touch. Typing asynchronous action creators is a bit of a challenge and there may well be a better approach but it does the job pretty well.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK