react-redux-typescript-guide README

The project's README from github.com/piotrwitek/react-redux-typescript-guide. As one markdown file: README.md.

React & Redux in TypeScript - Complete Guide

"This guide is a living compendium documenting the most important patterns and recipes on how to use React (and its Ecosystem) in a functional style using TypeScript. It will help you make your code completely type-safe while focusing on inferring the types from implementation so there is less noise coming from excessive type annotations and it's easier to write and maintain correct types in the long run."

Join the community on Spectrum Join the chat at https://gitter.im/react-redux-typescript-guide/Lobby

Found it useful? Want more updates?

Show your support by giving a :star:

Buy Me a Coffee Become a Patron



What's new?

:tada: Now updated to support TypeScript v4.6 :tada: :rocket: _Updated to [email protected] :rocket:



Goals

React, Redux, Typescript Ecosystem

Examples

Playground Project

Build Status

Check out our Playground Project located in the /playground folder. It contains all source files of the code examples found in the guide. They are all tested with the most recent version of TypeScript and 3rd party type-definitions (like @types/react or @types/react-redux) to ensure the examples are up-to-date and not broken with updated definitions (It's based on create-react-app --typescript).

Playground project was created so that you can simply clone the repository locally and immediately play around with all the component patterns found in the guide. It will help you to learn all the examples from this guide in a real project environment without the need to create complicated environment setup by yourself.

Contributing Guide

You can help make this project better by contributing. If you're planning to contribute please make sure to check our contributing guide: CONTRIBUTING.md

Funding

You can also help by funding issues. Issues like bug fixes or feature requests can be very quickly resolved when funded through the IssueHunt platform.

I highly recommend to add a bounty to the issue that you're waiting for to increase priority and attract contributors willing to work on it.

Let's fund issues in this repository


🌟 - New or updated section

Table of Contents


Installation

Types for React & Redux

npm i -D @types/react @types/react-dom @types/react-redux

"react" - @types/react
"react-dom" - @types/react-dom
"redux" - (types included with npm package)*
"react-redux" - @types/react-redux

*NB: Guide is based on types for Redux >= v4.x.x.

⇧ back to top


React Types Cheatsheet

React.FC<Props> | React.FunctionComponent<Props>

Type representing a functional component

const MyComponent: React.FC<Props> = ...

React.Component<Props, State>

Type representing a class component

class MyComponent extends React.Component<Props, State> { ...

React.ComponentType<Props>

Type representing union of (React.FC<Props> | React.Component<Props>) - used in HOC

const withState = <P extends WrappedComponentProps>(
  WrappedComponent: React.ComponentType<P>,
) => { ...

React.ComponentProps<typeof XXX>

Gets Props type of a specified component XXX (WARNING: does not work with statically declared default props and generic props)

type MyComponentProps = React.ComponentProps<typeof MyComponent>;

React.ReactElement | JSX.Element

Type representing a concept of React Element - representation of a native DOM component (e.g. <div />), or a user-defined composite component (e.g. <MyComponent />)

const elementOnly: React.ReactElement = <div /> || <MyComponent />;

React.ReactNode

Type representing any possible type of React node (basically ReactElement (including Fragments and Portals) + primitive JS types)

const elementOrPrimitive: React.ReactNode = 'string' || 0 || false || null || undefined || <div /> || <MyComponent />;
const Component = ({ children: React.ReactNode }) => ...

React.CSSProperties

Type representing style object in JSX - for css-in-js styles

const styles: React.CSSProperties = { flexDirection: 'row', ...
const element = <div style={styles} ...

React.XXXHTMLAttributes<HTMLXXXElement>

Type representing HTML attributes of specified HTML Element - for extending HTML Elements

const Input: React.FC<Props & React.InputHTMLAttributes<HTMLInputElement>> = props => { ... }

<Input about={...} accept={...} alt={...} ... />

React.ReactEventHandler<HTMLXXXElement>

Type representing generic event handler - for declaring event handlers

const handleChange: React.ReactEventHandler<HTMLInputElement> = (ev) => { ... } 

<input onChange={handleChange} ... />

React.XXXEvent<HTMLXXXElement>

Type representing more specific event. Some common event examples: ChangeEvent, FormEvent, FocusEvent, KeyboardEvent, MouseEvent, DragEvent, PointerEvent, WheelEvent, TouchEvent.

const handleChange = (ev: React.MouseEvent<HTMLDivElement>) => { ... }

<div onMouseMove={handleChange} ... />

In code above React.MouseEvent<HTMLDivElement> is type of mouse event, and this event happened on HTMLDivElement

⇧ back to top


React

Function Components - FC

- Counter Component

import * as React from 'react';

type Props = {
  label: string;
  count: number;
  onIncrement: () => void;
};

export const FCCounter: React.FC<Props> = props => {
  const { label, count, onIncrement } = props;

  const handleIncrement = () => {
    onIncrement();
  };

  return (
    <div>
      <span>
        {label}: {count}
      </span>
      <button type="button" onClick={handleIncrement}>
        {`Increment`}
      </button>
    </div>
  );
};

⟩⟩⟩ demo

⇧ back to top

- Counter Component with default props

import * as React from 'react';

type Props = {
  label: string;
  count: number;
  onIncrement: () => void;
};

// React.FC is unaplicable here due not working properly with default props
// https://github.com/facebook/create-react-app/pull/8177
export const FCCounterWithDefaultProps = (props: Props): JSX.Element => {
  const { label, count, onIncrement } = props;

  const handleIncrement = () => {
    onIncrement();
  };

  return (
    <div>
      <span>
        {label}: {count}
      </span>
      <button type="button" onClick={handleIncrement}>
        {`Increment`}
      </button>
    </div>
  );
};

FCCounterWithDefaultProps.defaultProps = { count: 5 };

⟩⟩⟩ demo

⇧ back to top

- Spreading attributes in Component

import * as React from 'react';

type Props = React.PropsWithChildren<{
  className?: string;
  style?: React.CSSProperties;
}>;

export const FCSpreadAttributes: React.FC<Props> = (props) => {
  const { children, ...restProps } = props;

  return <div {...restProps}>{children}</div>;
};

⟩⟩⟩ demo

⇧ back to top


Class Components

- Class Counter Component

import * as React from 'react';

type Props = {
  label: string;
};

type State = {
  count: number;
};

export class ClassCounter extends React.Component<Props, State> {
  readonly state: State = {
    count: 0,
  };

  handleIncrement = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    const { handleIncrement } = this;
    const { label } = this.props;
    const { count } = this.state;

    return (
      <div>
        <span>
          {label}: {count}
        </span>
        <button type="button" onClick={handleIncrement}>
          {`Increment`}
        </button>
      </div>
    );
  }
}

⟩⟩⟩ demo

⇧ back to top

- Class Component with default props

import * as React from 'react';

type Props = {
  label: string;
  initialCount: number;
};

type State = {
  count: number;
};

export class ClassCounterWithDefaultProps extends React.Component<
  Props,
  State
> {
  static defaultProps = {
    initialCount: 0,
  };

  readonly state: State = {
    count: this.props.initialCount,
  };

  handleIncrement = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    const { handleIncrement } = this;
    const { label } = this.props;
    const { count } = this.state;

    return (
      <div>
        <span>
          {label}: {count}
        </span>
        <button type="button" onClick={handleIncrement}>
          {`Increment`}
        </button>
      </div>
    );
  }
}

⟩⟩⟩ demo

⇧ back to top


Generic Components

- Generic List Component

import * as React from 'react';

export interface GenericListProps<T> {
  items: T[];
  itemRenderer: (item: T) => JSX.Element;
}

export class GenericList<T> extends React.Component<GenericListProps<T>, {}> {
  render() {
    const { items, itemRenderer } = this.props;

    return (
      <div>
        {items.map(itemRenderer)}
      </div>
    );
  }
}

⟩⟩⟩ demo

⇧ back to top


Hooks

https://reactjs.org/docs/hooks-intro.html

- useState

https://reactjs.org/docs/hooks-reference.html#usestate

import * as React from 'react';

type Props = { initialCount: number };

export default function Counter({initialCount}: Props) {
  const [count, setCount] = React.useState(initialCount);
  return (
    <>
      Count: {count}
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
    </>
  );
}

⇧ back to top

- useContext

https://reactjs.org/docs/hooks-reference.html#usecontext

import * as React from 'react';
import ThemeContext from '../context/theme-context';

type Props = {};

export default function ThemeToggleButton(props: Props) {
  const { theme, toggleTheme } = React.useContext(ThemeContext);
  return (
    <button onClick={toggleTheme} style={theme} >
      Toggle Theme
    </button>
  );
}

⇧ back to top

- useReducer

https://reactjs.org/docs/hooks-reference.html#usereducer

import * as React from 'react';

interface State {
  count: number;
}

type Action = { type: 'reset' } | { type: 'increment' } | { type: 'decrement' };

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    case 'reset':
      return { count: 0 };
    default:
      throw new Error();
  }
}

interface CounterProps {
  initialCount: number;
}

function Counter({ initialCount }: CounterProps) {
  const [state, dispatch] = React.useReducer(reducer, {
    count: initialCount,
  });

  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </>
  );
}

export default Counter;

⇧ back to top


Render Props

<https://reactjs.org/docs/rend

react-redux-typescript-guide (piotrwitek/react-redux-typescript-guide)

Searching Database…

What is this?A living guide collecting the most important patterns and recipes for using React and Redux in a fully type-safe way with TypeScript, favoring type inference over manual type annotations.
Is this a library I install, or just a reference guide?It is a documentation-style guide with code snippets, not an npm package to install. It does ship a companion Playground Project you can clone to run every example locally.
How is the guide organized?It is structured as a table of contents starting with a React Types Cheatsheet, then Function/Class/Generic Components, Hooks, Render Props, HOCs, and a full Redux section (store, actions, reducers, epics, selectors, react-redux), ending with Configuration & Dev Tools and an FAQ.
Where should a beginner start?New readers are pointed to clone the repo and use the Playground Project, which is based on create-react-app --typescript and contains runnable versions of every example in the guide.
Is the guide still up to date?The most recent noted update brought it in line with TypeScript v4.6 and typesafe-actions v5, and it assumes Redux v4.x type definitions; the README does not mention newer TypeScript or Redux versions.
What is the Playground Project for?It is a companion project in the /playground folder, built on create-react-app --typescript, containing runnable versions of every code example in the guide so you can experiment without setting up your own environment.
What TypeScript libraries does the guide recommend alongside React and Redux?It highlights typesafe-actions for type-safe Redux action creators, utility-types as a lodash-like collection of reusable generic types, and react-redux-typescript-scripts for shared dev-tooling config across projects.
Does it cover async Redux patterns like Observables?Yes, it includes a dedicated section on async flow with redux-observable (typing and testing epics) plus typing selectors with reselect, in addition to standard store and reducer typing.
Are there full example apps to look at?The guide links to a Todo-App Codesandbox playground and a TypeScript RealWorld App with its own GitHub repo and live demo for a more complete, real-world reference.
Creator
piotrwitek
Size
2.7 MB
Stars
13.3K
Updated
CommitMay 09
Age
9y
Commits · 90 Days