Taking React and Redux to the next level with Typescript

Taking React and Redux to the next level with Typescript

Prologue

If you ever used Redux before you know that much of the ways we write Redux logic and why it works relies on us knowing the shape of our state ahead of time. That need is very much inline with how good typescript code forces us to define the shape of our functions and variables before we can build the output JavaScript code.

As I'll be making heavy use of Redux in the near future and I haven't done much with it for some time, I decided to go through Level Up Tutorials' (LUT) React and Redux For Everyone course to refresh my memory on the many concepts around Redux. To add some spice to it, and because I love TS, this time I decided I'd write the tutorial app in Typescript.

This post is a collection of thoughts and highlights of my experience.

Some example code

You can see the code for the course, and each step of my way via git tags, on my github. I've also created a CodeSandbox which contains a minimal setup for react-redux and a connected component using Typescript.

You're free to look through them or use them as inspiration for your own code. I'll mostly use the repo on Github here to illustrate some points.

Defining the global state and root reducer

In my repo I had two reducers being merged by combineReducers, their state is defined as follows:

  • movies
export interface IReduxMoviesState {
  movies: IMovie[];
  moviesLoaded: boolean;
  moviesLoadedAt?: number;
  movie?: IMovie;
  movieLoaded: boolean;
}
  • toggle
export interface IReduxMessageState {
  messageVisibility: boolean;
}

With our reducers returning each of these states, we can define the global app state like:

const rootReducer = combineReducers({
  toggle,
  movies
});

export type AppState = ReturnType<typeof rootReducer>;

This makes the AppState look like:

type AppState = {
toggle: IReduxMessageState;
movies: IReduxMoviesState;
};

This is great because everywhere our redux state is used, we know exactly what it looks like and what we can reference from it when connecting components.

Defining action creators and action type constants

It's common practice in Redux to have action types being defined as constants. Because we're using Typescript, we can make use of enums and extending interfaces to make our code more descriptive. In my repo I have the following enum for action types:

export enum EReduxActionTypes {
GET_MOVIE = 'GET_MOVIE',
GET_MOVIES = 'GET_MOVIES',
RESET_MOVIE = 'RESET_MOVIE',
TOGGLE_MESSAGE = 'TOGGLE_MESSAGE'
}

If you're familiar with Typescript you'll see that I made the enums have defined values. This is to avoid the enum keys being assigned numerical values which could possibly make the code less resilient. Either way, this will make defining our action creators a little easier.

I defined the actions basing myself on an interface with a more generic typevalue, it is pretty bare bones but it allows for great scalability:

export interface IReduxBaseAction {
type: EReduxActionTypes;
}

For example, in the case of the movies reducer, there are a few different actions that can be dispatched:

export interface IReduxGetMoviesAction extends IReduxBaseAction {
type: EReduxActionTypes.GET_MOVIES;
data: IMovie[];
}
export interface IReduxGetMovieAction extends IReduxBaseAction {
type: EReduxActionTypes.GET_MOVIE;
data: IMovie;
}

export interface IReduxResetMovieAction extends IReduxBaseAction {
type: EReduxActionTypes.RESET_MOVIE;
}

As with many things in Typescript, you don't need to know how the values for data are defined, all you need to know in this case is that each action will contain the correct type of object or array for the data property of our action.

By aggregating those types into a union type, I can write my movies reducer like the below:

type TMoviesReducerActions = IReduxGetMoviesAction | IReduxGetMovieAction | IReduxResetMovieAction;

export default function(state: IReduxMoviesState = initialState, action: TMoviesReducerActions) {
switch (action.type) {
case EReduxActionTypes.GET_MOVIES:
return { ...state, movies: action.data, moviesLoaded: true, moviesLoadedAt: Date.now() };
case EReduxActionTypes.GET_MOVIE:
return { ...state, movie: action.data, movieLoaded: true };
case EReduxActionTypes.RESET_MOVIE:
return { ...state, movie: undefined, movieLoaded: false };
default:
return state;
}
}

This reducer is one of my favourite parts of this TS and Redux implementation.

Because I use different values of EReduxActionTypes for each action. when I get action.data within the different case's, Typescript already knows that data is of the correct type, i.e. Imovie for IReduxGetMovieAction and IMovie[] (an array of movies) for IReduxGetMoviesAction.

This is a VERY POWERFUL THING.

In my tutorial app, the reducers are fairly simple but we can already see that scaling this wouldn't be much of an issue and wouldn't really increase the complexity of our store that much.

This is specially true if we take into account the excellent developer experience that VS Code offers to us for Typescript.

Connecting components and using our store's state

To connect our movies state with a MoviesList component, the code used is as follows:

const mapStateToProps = (state: AppState) => ({
movies: state.movies.movies,
isLoaded: state.movies.moviesLoaded,
moviesLoadedAt: state.movies.moviesLoadedAt
});

const mapDispatchToProps = (dispatch: Dispatch<AnyAction>) =>
bindActionCreators(
{
getMovies
},
dispatch
);

export default connect(
mapStateToProps,
mapDispatchToProps
)(MoviesList);

There's quite a bit of code and re-assignment of values in here. Usually this could lead to some confusion as to which props are going to be available to our MoviesList component but Typescript will make sure that doesn't happen by letting us parse the type definitions of mapStateToProps and mapDispatchToProps and use it when creating our component:

class MoviesList extends PureComponent<ReturnType<typeof mapStateToProps> & ReturnType<typeof mapDispatchToProps>, {}> {
// Code for the component goes here
}

We could even simplify things slightly by creating a MoviesList props type like so:

type TMoviesListProps = ReturnType<typeof mapStateToProps> & ReturnType<typeof mapDispatchToProps>;

class MoviesList extends PureComponent<TMoviesListProps, {}> {
// Code for the component goes here
}

Now, if we try to reference anything from this.props inside our component, we will have full visibility of all the properties supplied to us by mapStateToProps and mapDispatchToProps.

Conclusion

Even though managing state with Redux and following its standard practices can lead us to spread logic through a number of files and/or add, an arguably large, amount of boilerplate code. By making use of Typescript, we can greatly increase the readability of our code and likely make it easier for anyone that may not be as aware of the ins and outs of a complex application, what each of its parts is responsible for and what they expect to receive from other components.

The tutorial application may not be the most complex one and maybe I didn't make the most elaborate use of Typescript. I still would like to think that it highlights some of Typescript's power and why more and more people are starting to look into it recently.

What do you think about Typescript and how it can change our developer experience when creating and scaling applications? Feel free to comment below or reach out to me on social media, details can be found on my website: leomeloxp.dev.

One last thing. When writing this app I tried to keep the code as close to the original code written in LUT's React and Redux for Everyone course as possible.

Thanks for reading. If you liked this post, share it with all of your programming buddies!

Originally published on dev.to


React-Redux with TypeScript

React-Redux with TypeScript

TypeScript is a typed superset of JavaScript. It has become popular recently in applications due to the benefits it can bring. If you are new to TypeScript it is highly recommended to become familiar with it first before proceeding.

TypeScript is a typed superset of JavaScript. It has become popular recently in applications due to the benefits it can bring. If you are new to TypeScript it is highly recommended to become familiar with it first before proceeding.

TypeScript is a great language to choose if you are a JavaScript developer and interested in moving towards a statically typed language. Using TypeScript is such a logical move for developers that are comfortable with JavaScript but haven’t written in languages that are statically typed (like C, JVM’s, Go, etc).

As I began my journey to TypeScript, (I’m most comfortable with JS, but have written a little bit in Go and C), I found it to be pretty easy to pick up. My initial thought was: “It really isn’t that bad typing all the argument in my function and typing the return value; what’s the fuss all about?” It was nice and simple until we had a project where we needed to create a React/Redux app in TypeScript.

It’s super easy to find material for React + JS, but as you begin to search for React + TS and especially React + Redux + TS, the amount of online tutorials (including YouTube videos) begin to dwindle significantly. I found myself scouring Medium, Stack Overflow, etc. for anything I could find to help explain how to set up the project, how types are flowing between files (especially once Redux is involved), and how to build with Webpack. This article is a way for me to solidify my knowledge of React + Redux + TS, and to hopefully provide some guidance for anyone else who is interested in using this tech stack for the front end. TypeScript is becoming more popular, so I hope this is useful to others in the community.

Prerequisites: I assume you’re aware of how React, Redux, and Webpack work, and also most concepts in TypeScript (at least interfaces and generics).

What are we gonna build? Just to keep it simple, we’ll build the infamous to-do list application. Remember that the purpose is to understand how to set up the project and know how TypeScript integrates with React and Redux. The features that this application will support are:

  1. Add a new item to a list.
  2. Remove an item from the list.

The code for the project can be found here: https://github.com/sterlingdeng/react-redux-ts-boilerplate.

For my project, I didn’t use create-react-app --typescript to get the project started. I found that it was a valuable learning experience to get it started from scratch. I’ll go step by step through the import files and folders needed to get this project up and running. Before we start, let me show you what the final structure looks like.

TS-Redux-React-Boilerplate
├── build
├── node_modules
├── public
│   ├── bundle.js
│   └── index.html
├── src
│   ├── App.tsx
│   ├── index.tsx
│   ├── actions
│   │   ├── actions.ts
│   │   └── index.ts
│   ├── components
│   │   ├── index.ts
│   │   └── TodoItem.tsx
│   ├── containers
│   │   └── TodoContainer.tsx
│   ├── reducers
│   │   ├── index.ts
│   │   └── todoReducers.ts
│   ├── store
│   │   └── store.ts
│   └── types
│       └── types.d.ts
├── .gitignore
├── package-lock.json
├── package.json
├── tslint.json
├── tsconfig.json
├── webpack.config.js
└── README.md

First, let’s look at the package.json file and install these dependencies using npm i.

"dependencies": {
    "@types/node": "^12.0.0",
    "@types/react": "^16.8.15",
    "@types/react-dom": "^16.8.4",
    "@types/react-redux": "^7.0.8",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-redux": "^7.0.3",
    "redux": "^4.0.1",
    "ts-loader": "^5.4.5",
    "typesafe-actions": "^4.2.0",
    "typescript": "^3.4.5",
    "webpack": "^4.30.0",
    "webpack-cli": "^3.3.1"
  }

Let’s first look at the dependencies with the format @types/[npm module here]. If you aren’t familiar with what these modules are, look up https://github.com/DefinitelyTyped/DefinitelyTyped. Since most modules are written in JavaScript, they aren’t written with proper type definitions. Once you attempt to import a module without any type information into a project that is all typed, TypeScript will complain. To prevent this, the community of contributors at DefinitelyTyped create high-quality type definition of the most commonly used JavaScript modules so that those modules will integrate as seamlessly as possible with TS.

You are probably familiar with the next four. ts-loader is needed because Webpack needs a plugin to parse .ts and .tsx files. (This is similar to babel.)

typesafe-actions is a library I use with Redux + TypeScript. Without it, the files can get quite noisy in terms of declaring types for the Store, Reducer, and Actions. This library provides methods that infer type definition for redux code so that the files are a bit cleaner and focused.

webpack and webpack-cli are used to bundle the .ts and .tsx files into one.js file that can be sent to the front end.

Next, let’s look at thetsconfig.json file. The purpose of this file is to configure how you want the ts compiler to run.

{
  "compilerOptions": {
    "baseUrl": ".",
    "outDir": "build/dist",
    "module": "commonjs",
    "target": "es5",
    "sourceMap": true,
    "allowJs": true,
    "jsx": "react"
  }
}

baseUrl denotes the folder. outDir directs the compiler where it should put the compiled code. module tells the compiler which JavaScript module types to use. target tells the compiler which JS version to target. sourceMap tells the compiler to create a bundle.js.map along with bundle.js. Because bundling will turn multiple files into one large .js file, troubleshooting code will be difficult because you wouldn’t easily know which file and at which line the code failed (since everything is just one big file). The .map file will map the bundled file to the respective unbundled file.

tslint.json provides options on how strict or loose you want the ts linter to be. The various options you can set for the linter can be found online.

Action Creators

Normally when I start projects with Redux, I begin at the action creators. Let’s quickly review the features that we need to implement: Adding an item to a list and removing an item from the list. This means we’ll need two action creators, one for adding and another for removing.

import { action } from "typesafe-actions";

// use typescript enum rather than action constants
export enum actionTypes {
  ADD = "ADD",
  DELETE = "DELETE"
}

export const todoActions = {
  add: (item: string) => action(actionTypes.ADD, item),
  delete: (idx: number) => action(actionTypes.DELETE, idx)
};

In the actions.ts file, I’m using the enum feature in TS to create the action constants. Secondly, I’m using the action method provided from the typesafe-actions module. The first argument you pass into the method is a string which represents that action, and the second argument is the payload. The add method will add an item to the list of to-dos, and the deletemethod will remove an item, based on the provided index, from the list of to-dos.

In terms of type safety, what we want in our reducers file is the proper type of the payload, given a specific action. The feature in TypeScript that provides this support is called discriminated union and type guarding. Consider the example below:

// this is an example of discriminated unions
// this file isn't used in the project

interface ActionAdd {
  type: "ADD",
  payload: string
}

interface ActionDelete {
  type: "DELETE",
  payload: number
}

type Actions = ActionAdd | ActionDelete

function reducer(a: Actions) {
  switch(a.type) {
    case "ADD" : {
      // payload is a string
    }
    case "DELETE" : {
      // payload is a number
    }
  }
}

Given the shape of the two action objects, we can discriminate between them based on the type property. Using control flow analysis, like if-else or switch-case statements, it’s very logical that in line 16, the only type that the payload can be is a string. Since we only have two actions, the remaining payload in line 22 will be a number. If you are interested in learning more about discriminated unions and type guarding, I would recommend learning more about it here and here.

Reducer

After defining our action creators, let’s create the reducer for this project.

import * as MyTypes from "MyTypes";
import { actionTypes } from "../actions/";

interface ITodoModel {
  count: number;
  list: string[];
}

export const initialState: ITodoModel = {
  count: 2,
  list: ["Do the laundry", "Do the dishes"]
};

export const todoReducer = (state: ITodoModel = initialState, action: MyTypes.RootAction) => {
  switch (action.type) {
    case actionTypes.ADD: {
      return {
        ...state,
        count: state.count + 1,
        list: [...state.list, action.payload]
      };
    }
    case actionTypes.DELETE: {
      const oldList = [...state.list];
      oldList.splice(action.payload, 1);
      const newList = oldList;

      return {
        ...state,
        count: state.count - 1,
        list: newList
      };
    }
    default:
      return state;
  }
};

In lines four through seven, I’m defining the model (or schema) of our Todostore. It will keep track of how many to-do items we have, as well as the array of strings. Lines nine through 12 are the initial state when the application first starts. Within the todoReducer function, we want type safety within the casestatements. Based on the earlier gist, we accomplished that by discriminated unions and type guarding, done by typing the action parameter. We have to first define an interface for every action object, and then create a union of them all and assign that to a type. This can get tedious if we have a lot of action creators — luckily, typesafe-actions has methods to help create the proper typing of the action creators without having to actually write out all the interfaces.

declare module "MyTypes" {
  import { StateType, ActionType } from "typesafe-actions";
  // 1 for reducer, 1 for action creators
  export type ReducerState = StateType<typeof import("../reducers").default>;
  export type RootAction = ActionType<typeof import("../actions/actions")>;
}

Ignoring line four for now and focusing on line five, we use a method called ActionType from the module, and import the actions from actions.ts to create the discriminated union types, which are then assigned to a type called RootAction. In line one of the todoReducers.ts, we import MyTypes and in line 14, we type the action parameter with MyTypes.RootAction. This allows us to have IntelliSense and autocompletion within the reducers!

Now that we have the reducer set up, the ReducerState type from the types.d.ts file allows TypeScript to infer the shape of the state object in the reducer function. This will provide IntelliSense when we try to access the payload object within the Reducer. An example of what that looks like is in the picture below.

IntelliSense in the Reducer

Redux Store

Finally, let’s hook up the reducer to the redux store.

import { createStore } from "redux";
import rootReducer from "../reducers";

const store = createStore(rootReducer);

export default store;

Let’s recap what we have accomplished up until this point. We have created and typed our action creators using the action method from typesafe-actions. We have created our types.d.ts file which provide type information on our action creators and reducer state. The reducer has been created and the actions are typed by using MyTypes.RootAction, which provide invaluable auto-completion information of the payload within the reducer’s case statements. And lastly, we created our Redux store.

React and TS

Let’s change gears and begin working on creating and typing our React components. I’ll go over examples of how to properly type both function and class based components, along with instructions on how to type both the props and state (for stateful components).

App.tsx

import * as React from "react";
import TodoContainer from "./containers/TodoContainer";

export const App: React.FC<{}> = () => {
  return (
    <>
      <h1>React Redux Typescript</h1>
      <TodoContainer />
    </>
  );
};

App is a functional component that is typed by writing const App: React.FC<{}>. (FC refers to functional component.) If you aren’t familiar with generics (which is the <{}> ), I think of them like variables but for types. Since the shape of props and state can differ based on different use cases, generics are a way for us to, well, make the component generic! In this case, App doesn’t take any props; therefore, we pass in an empty object as the generic. How do we know that the generic is specifically for props? If you use VS code, IntelliSense will let you know what type it needs.

Where it says <P = {}> , it means type {} has been assigned to P, where Pstands for props. For class-based components, React will use S to refer to state. App is a functional component that receives no props and is not connected to the Redux store. Let’s go for something a little more complicated.

TodoContainer.tsx

import * as React from "react";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import * as MyTypes from "MyTypes";
import { actionTypes } from "../actions";
import { TodoItem } from "../components";

interface TodoContainerState {
  todoInput: string;
}

interface TodoContainerProps {
  count: number;
  todoList: string[];
  addToDo: (item: string) => object;
  deleteToDo: (idx: number) => object;
}

class TodoContainer extends React.Component<TodoContainerProps, TodoContainerState> {
  constructor(props) {
    super(props);
    this.state = {
      todoInput: ""
    };
  }

  handleTextChange = e => {
    this.setState({
      todoInput: e.target.value
    });
  };

  handleButtonClick = () => {
    this.props.addToDo(this.state.todoInput);
    this.setState({
      todoInput: ""
    });
  };

  handleDeleteButtonClick = (idx: number) => {
    console.log("deleting", idx);
    this.props.deleteToDo(idx);
  };

  render() {
    let todoJSX: JSX.Element[] | JSX.Element;
    if (!this.props.todoList.length) {
      todoJSX = <p>No to do</p>;
    } else {
      todoJSX = this.props.todoList.map((item, idx) => {
        return (
          <TodoItem item={item} key={idx} idx={idx} handleDelete={this.handleDeleteButtonClick} />
        );
      });
    }

    return (
      <div>
        {todoJSX}
        <input
          onChange={this.handleTextChange}
          placeholder={"New To Do Here"}
          value={this.state.todoInput}
        />
        <button onClick={this.handleButtonClick}>Add To Do</button>
      </div>
    );
  }
}

const MapStateToProps = (store: MyTypes.ReducerState) => {
  return {
    count: store.todo.count,
    todoList: store.todo.list
  };
};

const MapDispatchToProps = (dispatch: Dispatch<MyTypes.RootAction>) => ({
  addToDo: (item: string) => dispatch({ type: actionTypes.ADD, payload: item }),
  deleteToDo: (idx: number) => dispatch({ type: actionTypes.DELETE, payload: idx })
});

export default connect(
  MapStateToProps,
  MapDispatchToProps
)(TodoContainer);

OK, TodoContainer.tsx is the most complicated of them all, but I’ll walk you through what’s going on in the code. TodoContainer is a React Class Component because I need it to hold in its state the value for the input box. It is also connected to the redux store, so it’ll have MapStateToProps and MapDispatchToProps . First, I’ve definedTodoContainerState . Since I’ll be holding the value of the input box in state, I’ll type the property as a string.

Next, I’ve defined TodoContainerProps, which will be the shape of the Container’s props.

Because class-based components can have both state and props, we should expect that there should be at least two generics that we need to pass into React.Component.

P for Props and S for State

If you mouse over React.Component, you can see that it takes in three generics, P, S, and SS. The first two generics are props and state. I’m not quite sure whatSS is and what the use case is. If anyone knows, please let me know in the comments below.

After passing in the generics into React.Component , IntelliSense and autocompletion will work within this.state and for props.

Next, we want to type MapStateToProps and MapDispatchToProps. This is easily achievable by leveraging the MyTypes module that we built in the redux section. For MapStateToProps, we assign the store type to be MyTypes.ReducerState. An example of the IntelliSense it will provide is in the below screenshot.

IntelliSense for MapStateToProps

Lastly, we want to have type safety within MapDispatchToProps. The benefit that is provided is a type-safe payload given an action type.

Type-safe payloads

In the screenshot above, I purposely typed item as a boolean. Immediately, the TSServer will pick up that the boolean payload within MapDispatchToProps is not correct because it’s expecting the payload to be a string, given that the type is actionTypes.ADD. TodoContainer.tsx has the most going on since it is a class based React component, with both state and props, and is also connected to the store.

Before we wrap up, let’s look at our last component: TodoItem.tsx

TodoItem.tsx

This component is a functional component with props — code below.

import * as React from "react";

interface TodoItemProps {
  item: string;
  idx: number;
  handleDelete: (idx: number) => void;
}

export const TodoItem: React.FC<TodoItemProps> = props => {
  return (
    <span>
      {props.item}
      <button onClick={() => props.handleDelete(props.idx)}>X</button>
    </span>
  );
};

The shape of the props are defined in the interface TodoItemProps. The type information is passed into as a generic in React.FC. Doing so will provide auto-completion for props within the component. Awesome.

Another great feature that TypeScript provides when used with React is IntelliSense for props when rendering React Components within JSX. As an example, if you delete idx:number from TodoItemProps and then you navigate to TodoContainer.tsx, an error will appear at the place where you render <TodoItem />.

Property ‘idx’ does not exist

Because we removed idx from the TodoItemProps interface, TypeScript is letting us know that we have provided an additional prop that it couldn’t find, idx, into the component.

Lastly, let’s build the project using Webpack. In the command line, type npm run build. In the public folder within the root directory, you should see bundle.js alongsideindex.html. Open index.html in any browser and you should see a very simple, unstyled, to-do app.

After webpack build

I hope that I was able to demonstrate the power of TypeScript coupled with React and Redux. It may seem a bit overkill for our simple to-do list app — you just need to imagine the benefit of TS + React + Redux at scale. It will help new developers read the code quicker, provide more confidence in refactoring, and ultimately improve development speed.

If you need more reference and material, I used the following two Git repos to teach myself

Both these repos have proved invaluable for my learning, and I hope they will be the same for you.

Using Typescript with React and Redux

Using Typescript with React and Redux

Redux with Typescript. Step 1: Typing the Store. Firstly, we will want to define an interface for our Redux store. Step 2: Defining action types and actions. Action types can be defined using a const & type pattern. Step 3: Defining Reducers. Step 4: Creating the initial Store.

Supercharge the stability of your React apps, with Typescript

Typescript is in its strongest position ever for making your React apps more stable, readable and manageable. The package has been steadily introducing more support for React and Javascript front-end frameworks; features have been ramping up specifically for them since version 3.0 and 3.1. Integration with React used to be a headache-inducing task, but is now a straight forward process that we will talk through in this article.

Create React App now has Typescript support built into the package, since react-scripts 2.1. It can simply be included in a new project with the --typescript flag. We will specifically be using CRA for bootstrapping a React based app with Typescript, before exploring how to integrate types and interfaces into React props and state, followed by a Redux setup.

To read more about Typescript, why it is used and its capabilities, take a look at my Typescript with NodeJS article, that aims to introduce the package and provide integration steps for NodeJS

Installing Create React App with Typescript

The Create React App website has a dedicated page specifically for documenting the installation process as well as migration steps for adding Typescript support for existing apps. To create a new app with Typescript included, run the following:

yarn create react-app app_name --typescript

#or
npx create-react-app app_name --typescript

There are a couple of noticeable changes from the Javascript based CRA boilerplate:

  • Included now is a tsconfig.json file configuring the Typescript compiler options.
  • .js files are now .tsx files. The Typescript compiler will pick up all .tsxfiles at compile time.
  • package.json contains dependencies for @types packages, including support for node, jest, react and react-dom out of the box.
  • react-app-env.d.ts file to reference react-scripts types. This file is automatically generated upon starting the development server, with yarn start.

Running yarn start at this stage will compile and run your app, yielding the identical bootstrapped app in your browser to the original Javascript-only Create React App counterpart.

Before we continue, it is worth stopping the development server and revisiting some linting tools specifically for Typescript and React development. I will be focusing on Sublime Text 3 — being my IDE of choice — but the general concepts of linting apply to all IDEs. Let’s briefly explore the tools.

Installing TSLint-React

Installing linting tools are extremely helpful with Typescript and React; you will find yourself referring to tooltips to obtain a certain type, especially with events. The linting tools are extremely strict with their default setup, so we will omit some of these rules in the installation procedure.

Note: These tools are installed via NPM or Yarn and are not tied to a specific IDE. We will firstly install these tools before the specific packages for Sublime Text 3.

Install typescript,tslint and tslint-react packages globally:

yarn global add tslint typescript tslint-react

Now inside your project directory, initialise tslint:

tslint --init

This command will generate a tslint.json file with some default options. Replace this file with the following:

{
"defaultSeverity": "error",
"extends": [
"tslint-react"
],
"jsRules": {
},
"rules": {
"member-access": false,
"ordered-imports": false,
"quotemark": false,
"no-console": false,
"semicolon": false,
"jsx-no-lambda": false
},
"rulesDirectory": [
],
"linterOptions": {
"exclude": [
"config//*.js",
"node_modules/
/*.ts"
]
}
}

To summarise what is happening in this file:

  • defaultSeverity is the level at which errors will be treated. error being the default value will yield red errors within your IDE, whereas warningwould display orange warnings.
  • "extends": ["tslint-react"]: The rules we are extending from are soley React based — we have removed the tslint-recommended library, some rules of which do not adhere to React syntax.
  • "rules": { "rule-name": false, ...}: We can omit rules in the rulesblock. For example, omiting the member-access rule will stop tslint from reporting that we are missing access types from our functions, (public, private…) — syntax that is not commonly used in React. Another example, ordered-imports, prompts us to order our import statements alphabetically. Check out all the rules available here.
  • "linterOptions": {"exclude": [...]}: Here we are excluding all Javascript files in the config directory and Typescript files within node_modules from TSLint checking.
Sublime Text 3 Packages

As the final step for setting up the development environment, install the SublimeLinter followed by SublimeLinter-tslint packages via PackageControl. Upon restarting Sublime Text the tools will be readily available.

Note: You may receive an error to say that the tslint module was not found upon restarting Sublime Text. If this was the case, re-install the packages locally rather than globally, that we opted for (and what documentation suggests) previously:

yarn add tslint tslint-react

VS Code Extension

For the Visual Studio Code editor, install the TSLint extension for full Typescript support.

With your IDE now ready to handle Typescript development, let’s dive into some code, visiting how to add interfaces and types to props and state within React.

Interfaces and Types for Props and State

We can apply interfaces and types for our props and state of our components.

Defining interfaces

Applying an interface to components will force us to adhere to such data structures when passing props into a component, ensuring that they are all accounted for while also stopping unwanted props to be passed down.

Interfaces can be defined outside of a component or imported from a separate file. Define an interface like so:

interface FormProps {
first_name: string;
last_name: string;
age: number;
agreetoterms?: boolean;
}

Here I have created a FormProps interface consisting of a few values. agreetoterms is optional, hence the ? after the name. We can also apply an interface for state:

interface FormState {
submitted?: boolean;
full_name: string;
age: number;
}

Note: Tslint used to prompt us to use a capital i in front of all our interface names, e.g. IFormProps and IFormState would be the above names. However, it is no longer enforced by default.

Applying interfaces to components

We can apply interfaces to both class components and stateless function components. For class components we utilise angle bracket syntax to apply our props and state interfaces respectively:

export class MyForm extends React.Component<FormProps, FormState> {
...
}

Note: In the event you have no props but would like to define state, you can place either {} or object in place of FormProps. Both values are valid empty objects.

And with function components we can pass our props interface, followed by any other arguments and their specific interfaces:

function MyForm(props: FormProps) {
...
}

Importing interfaces

Defining groups of interface definitions in one file is good practice; a common convention is to create a src/types/ folder with groups of your interfaces:

// src/types/index.tsx

export interface FormProps {
first_name: string;
last_name: string;
age: number;
agreetoterms?: boolean;
}

And to import your needed interfaces into your component files:

// src/components/MyForm.tsx

import React from 'react';
import { StoreState } from '../types/index';
...

Working with enums

Enums are another useful feature of Typescript. Lets say I wanted to define an enum for the MyForm component, and then check whether the submitted form value is valid:

// define enum
enum HeardFrom {
SEARCH_ENGINE = "Search Engine",
FRIEND = "Friend",
OTHER = "Other"
}
//construct heardFrom array
let heardFrom = [HeardFrom.SEARCH_ENGINE,
HeardFrom.FRIEND,
HeardFrom.OTHER];

//get submitted form value
const submitted_heardFrom = form.values.heardFrom;

//check if value is valid
heardFrom.includes(submitted_heardFrom)
? valid = true
: valid = false;

Working with iterables

We can also loop through iterables using for…of and for…in methods in Typescript. These two methods have one key difference:

  • Looping using for…of will return a list of values being iterated.
  • Looping using for…in will return a list of keys being iterated.
for (let i in heardFrom) {
console.log(i); // "0", "1", "2",
}

for (let i of heardFrom) {
console.log(i); // "Search Engine", "Friend", "Other"
}

Typing Events

In the event (no pun intended) you wish to type events, such as onChange or onClick events, utilise your syntax tools to obtain the exact event you need.

Consider the following example, where we update our state every time a name input is changed. By hovering your mouse over handleChange(), we can see clearly that the event type is indeed React.ChangeEvent<HTMLInputElement>:

Hovering over handleChange() to obtain the event type

This type is then used when typing the e argument in our handleChangefunction definition.

I have also typed the name and value objects of e with the following syntax:

const {name, value}: {name: string; value: string;} = e.target;

If you do not know what types an object specifies, then you can simply use the any type. We could have done this here:

const {name, value}: any = e.target;

Now we have covered some basics, we will next visit how to set up Redux with Typescript, and review more Typescript specific features along the way.

Redux with Typescript

If you would like to familiarise yourself with how Redux works with React, check out my introductory article:

Step 1: Typing the Store

Firstly, we will want to define an interface for our Redux store. Defining the expected state structure will be beneficial for your team and aid in maintaining the expected app state.

This can be done within the /src/types/index.tsx file we discussed earlier. Here is an example that deals with locality and authentication:

// src/types/index.tsx

export interface MyStore {
language: string;
country: string;
auth: {
authenticated: boolean;
username?: string;
};
}

Step 2: Defining action types and actions

Action types can be defined using a const & type pattern. We will firstly want to define the action types within a src/constants/index.tsx file:

// src/constants/index.tsx

export const SET_LANGUAGE = 'INCREMENT_ENTHUSIASM';
export type SET_LANGUAGE = typeof SET_LANGUAGE;
export const SET_COUNTRY = 'SET_COUNTRY';
export type SET_COUNTRY = typeof SET_COUNTRY;
export const AUTHENTICATE = 'AUTHENTICATE';
export type AUTHENTICATE = typeof AUTHENTICATE;

Notice how the constants we just defined are used as an interface type andas a string literal, which we will utilise next.

These const & type objects can now be imported into your src/actions/index.tsx file, where we can define action interfaces and the actions themselves, and typing them along the way:

// src/actions/index.tsx

import * as constants from '../constants';
//define action interfaces
export interface SetLanguage {
type: constants.SET_LANGUAGE;
language: string;
}
export interface SetCountry {
type: constants.SET_COUNTRY;
country: string;
}
export interface Authenticate{
type: constants.AUTHENTICATE;
username: string;
pw: string;
}
//define actions
export function setLanguage(l: string): SetLanguage ({
type: constants.SET_LANGUAGE,
language: l
});
export function setCountry(c: string): SetCountry ({
type: constants.SET_COUNTRY,
country: c
});
export function authenticate(u: string, pw: string): Authenticate ({
type: constants.SET_COUNTRY,
username: u,
pw: pw
});

Check out the authenticate action in particular here — we are passing a username and password, both of which are of type string, into the function. The return value is also typed, in this case as Authenticate.

Within the Authenticate interface we are also including the expected username and pw values for the action to be valid.

Step 3: Defining Reducers

To simplify the process of specifying an action type within a reducer, we can take advantage of union types, introduced in Typescript 1.4. A union type gives us the ability to combine 2 more more types into one type.

Back in our actions file, add a union type for locality under our interfaces:

// src/actions/index.tsx

export type Locality = SetLanguage | SetCountry;

Now we can apply this Locality type to our locality reducer action, in bold text below:

// src/reducers/index.tsx

import { Locality } from '../actions';
import { StoreState } from '../types/index';
import { SET_LANGUAGE, SET_COUNTRY, AUTHENTICATE} from '../constants/index';
export function locality(state: StoreState, action: Locality): StoreState {

switch (action.type) {
case SET_LANGUAGE:
return return { ...state, language: action.language};
case SET_COUNTRY:
return { ...state, language: action.country};
case AUTHENTICATE:
return {
...state,
auth: {
username: action.username,
authenticated: true
}
};
}
return state;
}

This reducer is relatively straight forward, but nonetheless fully typed:

  • This reducer, named locality, is typing our state as StoreState, and the expected action as a Locality type.
  • The reducer will return a StoreState object, if only just the original state in the event no actions are matched.
  • Our constant & type pairs are being utilised here too, as a means to switch between actions.

Step 4: Creating the initial Store

Now within your index.tsx we can initiate the store, utilising angle brackets again to pass the type in conjunction with createStore():

// src/index.tsx

import { createStore } from 'redux';
import { locality } from './reducers/index';
import { StoreState } from './types/index';
const store = createStore<StoreState>(locality, {
language: 'British (English)',
country: 'United Kingdom',
auth: {
authenticated: false
}
});

We are almost done — this covers most of our Redux integration. Let’s also visit mapStateToProps and mapDispatchToProps to cater for your container components.

Mapping State and Dispatch

Within mapStateToProps, remember to map the state argument with StoreState. The second argument, ownProps, can also be typed with a props interface:

// mapStateToProps example

import { StoreState } from '../types/index';
interface LocalityProps = {
country: string;
language: string;
}
function mapStateToProps (state: StoreState, ownProps: LocalityProps) ({
language: state.language,
country: state.country,
});

mapDispatchToProps is slightly different; we are utilising angle brackets again to pass an interface into the Dispatch method. Then, as expected, the return block dispatches our Locality type actions:

// mapDispatchToProps example

const mapDispatchToProps = {
actions.setLanguage,
actions.setCountry
}

Note: As we are wrapping these actions within connect(), it is not required to wrap our actions within dispatch(). We can also emit the parameters of our actions here.

Lastly, we can connect the two to our presentation component:

export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);
In Summary

This article has introduced Typescript in conjunction with React and how to utilise tslint-react for smoother development. We have visited how to interface and type your props and state throughout your components, as well as how to handle Typescript with events. Finally, we visited Typescript integration with Redux, integrating types throughout a Redux setup.

From here, you are now well placed to study the full feature set of Typescript in the official documentation.

Typescript does introduce additional consideration into your React projects, but the additional investment of supporting the language will ultimately improve the manageability of your apps as they scale.

Modularity and compartmentalising code is promoted by using Typescript; traits that medium to larger sized projects will want to adhere to. Keep this in mind as your projects grow: If you are finding maintainability a problem, Typescript could be the answer to improve readability while reducing the probability of errors throughout your codebase.

Thanks For Visiting, Keep Visiting. If you liked this post, share it with all of your programming buddies!

Originally published on medium.com

How to build a news app with JavaScript and React Native

How to build a news app with JavaScript and React Native

How to build a news app with JavaScript and React Native

Requirements for building the app:

  • A basic understanding of the JavaScript language.
  • Node.js, and react native.
  • Libraries used: moment, react-native, react-native-elements.

If you’re not familiar with these resources, don’t worry — they are quite easy to use.

The topics we will cover in the post are:

  • A basic understanding of the JavaScript language.
  • Node.js, and react native.
  • Libraries used: moment, react-native, react-native-elements.

And more…so let’s get started!

You can find the full project repo HERE.

News API

A simple and easy-to-use API that returns JSON metadata for headlines and articles live all over the web right now. — NewsAPI.org
First, you should go ahead and sign up for News Api to get your free apiKey (your authentication key).

Create a new React Native project, and call it news_app (or whatever you want). In the project directory, make a new folder and call it src . In srccreate a folder an name it components . So your project directory should look something like this:

In the src folder, create a new file called news.js . In this file we are going to fetch the JSON that contains the headlines from the News API.

news.js
const url =
  "https://newsapi.org/v2/top-headlines?country=us&apiKey=YOUR_API_KEY_HERE";

export async function getNews() {
  let result = await fetch(url).then(response => response.json());
  return result.articles;
}

Make sure you replace YOUR_API_KEY_HERE with your own API key. For more information about the News API, go to newsapi docs.

Now we declare the getNews function, which is going to fetch the articles for us. Export the function so we can use it in our App.js file.

App.js
import React from 'react';
import { FlatList } from 'react-native';

// Import getNews function from news.js
import { getNews } from './src/news';
// We'll get to this one later
import Article from './src/components/Article';

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { articles: [], refreshing: true };
    this.fetchNews = this.fetchNews.bind(this);
  }
  // Called after a component is mounted
  componentDidMount() {
    this.fetchNews();
   }

  fetchNews() {
    getNews()
      .then(articles => this.setState({ articles, refreshing: false }))
      .catch(() => this.setState({ refreshing: false }));
  }

  handleRefresh() {
    this.setState(
      {
        refreshing: true
    },
      () => this.fetchNews()
    );
  }

  render() {
    return (
      <FlatList
        data={this.state.articles}
        renderItem={({ item }) => <Article article={item} />}
        keyExtractor={item => item.url}
        refreshing={this.state.refreshing}
        onRefresh={this.handleRefresh.bind(this)}
      />
  );
  }
}

In the constructor, we define the initial state. articles will store our articles after we fetch them, and refreshing will help us in refresh animation. Notice that I setrefreshing to true, because when we start the app, we want the animation to start while we load the articles.

componentDidMount is invoked immediately after a component is mounted. Inside it we call the fetchNews method.

componentDidMount() {
  this.fetchNews();
}

In fetchNews we call getNews() which returns a promise. So we use the .then() method which takes a callback function, and the callback function takes an argument (the articles).

Now assign the articles in the state to the articles argument. I only typed articles because it’s a new ES6 syntax that means { articles: articles } , and we set refreshing to false to stop the spinner animation.

fetchNews() {
  getNews().then(
      articles => this.setState({ articles, refreshing: false })
  ).catch(() => this.setState({ refreshing: false }));
}

.catch() is called in rejected cases.

handleRefresh starts the spinner animation and call fetchNews(). We pass () => this.fetchNews() , so it’s called immediately after we assign the state.

handleRefresh() {
  this.setState({ refreshing: true },() => this.fetchNews());
}

In the render method, we return a FlatList element. Then we pass some props. data is the array of articles from this.state. The renderItem takes a function to render each item in the array, but in our case it just returns theArticle component we imported earlier (we’ll get there). And we pass the article item as a prop to use later in that component.

Article.js

In src/components create a new JavaScript file and call it Article.js.

Let’s start by installing two simple libraries using npm: react-native-elements, which gives us some premade components we could use, and moment that will handle our time.

run using terminal/cmd:

npm install --save react-native-elements moment

In Article.js:

import React from 'react';
import { View, Linking, TouchableNativeFeedback } from 'react-native';
import { Text, Button, Card, Divider } from 'react-native-elements';
import moment from 'moment';

export default class Article extends React.Component {
  render() {
    const {
      title,
      description,
      publishedAt,
      source,
      urlToImage,
      url
    } = this.props.article;
    const { noteStyle, featuredTitleStyle } = styles;
    const time = moment(publishedAt || moment.now()).fromNow();
    const defaultImg =
      'https://wallpaper.wiki/wp-content/uploads/2017/04/wallpaper.wiki-Images-HD-Diamond-Pattern-PIC-WPB009691.jpg';

    return (
      <TouchableNativeFeedback
        useForeground
        onPress={() => Linking.openURL(url)}
      >
        <Card
          featuredTitle={title}
          featuredTitleStyle={featuredTitleStyle}
          image={{
            uri: urlToImage || defaultImg
          }}
        >
          <Text style={{ marginBottom: 10 }}>
            {description || 'Read More..'}
          </Text>
          <Divider style={{ backgroundColor: '#dfe6e9' }} />
          <View
            style={{ flexDirection: 'row', justifyContent: 'space-between' }}
          >
            <Text style={noteStyle}>{source.name.toUpperCase()}</Text>
            <Text style={noteStyle}>{time}</Text>
          </View>
        </Card>
      </TouchableNativeFeedback>
    );
  }
}

const styles = {
  noteStyle: {
    margin: 5,
    fontStyle: 'italic',
    color: '#b2bec3',
    fontSize: 10
  },
  featuredTitleStyle: {
    marginHorizontal: 5,
    textShadowColor: '#00000f',
    textShadowOffset: { width: 3, height: 3 },
    textShadowRadius: 3
  }
};

There is a lot going on here. First, we start by destructuring the articleprop and the styles object defined below the class.

In render we define time to store the time for when the article was published. We use the moment library to convert the date to the time passed since then, and we pass publishedAt or time from now if publishedAt is null.

defaultImg is assigned an image URL in case the URL of the article image is null.

The render method returns TouchableNativeFeedback (use TouchableOpacityinstead if it does not work on your platform) to handle when the user presses the card. We pass it some props: useForground which tells the element to use the foreground when displaying the ripple effect on the card, and onPress , which takes a function and executes it when the user presses the card. We passed () => Linking.openUrl(url) which simply opens the URL to the full article when we press the card.

The card takes three props: featuredTitle which is just a fancy title placed over the image you could use title instead if you want, featuredTitleStyle to style it, and image which is the article image from the article prop. Otherwise, if its null , it’s going to be the defaultImg.

..
  featuredTitle={title}
  featuredTitleStyle={featuredTitleStyle}
  image={{ uri: urlToImage || defaultImg }}
..

As for the text element, it will hold the description for the article.

<Text style={{ marginBottom: 10 }}>{description}

We added a divider to separate the description from time and source name.

<Divider style={{ backgroundColor: '#dfe6e9' }} />

Below the Divider , we have a View that contains the source name and the time the article was published.

..
<View 
  style={{ flexDirection: ‘row’, justifyContent: ‘space-between’ }} > 
  <Text style={noteStyle}>{source.name.toUpperCase()}</Text>
  <Text style={noteStyle}>{time}</Text>
</View>
..

After the class, we defined the styles for these components.

Now if we run the app:

There you go! The source code for the app is available on GitHub HERE you can improve upon it and make a pull request😄.