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…

Что это такое?Живое руководство, в котором собраны самые важные шаблоны и рецепты использования React и Redux полностью типобезопасным способом с TypeScript. Предпочтение отдаётся выводу типов, а не ручным аннотациям типов.
Это библиотека, которую нужно установить, или просто справочник?Это руководство в формате документации с фрагментами кода, а не npm-пакет для установки. К нему прилагается сопутствующий проект Playground, который можно клонировать и запустить все примеры локально.
Как устроено руководство?Оно построено как оглавление: начинается с шпаргалки по типам React, затем идут функциональные, классовые и обобщённые компоненты, хуки, render props, HOC и большой раздел про Redux (store, actions, reducers, epics, selectors, react-redux). Завершается всё разделами о конфигурации и инструментах разработки и FAQ.
С чего новичку стоит начать?Новичкам предлагается клонировать репозиторий и воспользоваться проектом Playground, который основан на create-react-app --typescript и содержит рабочие версии каждого примера из руководства.
Руководство всё ещё актуально?Последнее отмеченное обновление привело его в соответствие с TypeScript v4.6 и typesafe-actions v5, а также подразумевает определения типов для Redux v4.x. В README не упоминаются более новые версии TypeScript или Redux.
Для чего нужен проект Playground?Это сопутствующий проект в папке /playground, созданный через create-react-app --typescript. В нём содержатся рабочие версии каждого примера кода из руководства, так что можно экспериментировать, не настраивая собственное окружение.
Какие TypeScript-библиотеки руководство рекомендует использовать вместе с React и Redux?Оно выделяет typesafe-actions для типобезопасных создателей действий Redux, utility-types, набор переиспользуемых обобщённых типов, похожий на lodash, и react-redux-typescript-scripts для общей конфигурации инструментов разработки в разных проектах.
Есть ли в руководстве асинхронные шаблоны Redux, например Observables?Да, там есть отдельный раздел об асинхронных потоках с redux-observable (типизация и тестирование эпиков), а также типизация селекторов с помощью reselect. Кроме того, описана стандартная типизация store и редьюсеров.
Есть ли полноценные примеры приложений для изучения?В руководстве есть ссылки на песочницу Todo-App в Codesandbox и на TypeScript RealWorld App с собственным репозиторием на GitHub и живой демо-версией. Это более полный пример из реальной практики.
Creator
piotrwitek
Size
2.7 MB
Stars
13.3K
Updated
CommitMay 09
Age
9y
Commits · 90 Days