Як використовувати Redux у вашому додатку React TypeScript

Redux - це передбачуваний контейнер стану для програм JavaScript. Це популярна бібліотека для управління станом у програмах React.

Redux може запропонувати кращий досвід для розробників, коли ви використовуєте його разом із TypeScript. TypeScript - це набір JavaScript, який перевіряє код, щоб зробити його надійним та зрозумілим.

У цьому посібнику я покажу вам, як використовувати Redux у вашому проекті React TypeScript, створивши додаток, що дозволяє додавати, видаляти та показувати статті.

Зануримось.

  • Передумови
  • Налаштовуючи
  • Створіть типи
  • Створіть типи дій
  • Створіть творців дій
  • Створіть редуктор
  • Створити магазин
  • Створіть компоненти

Передумови

Цей підручник передбачає, що ви принаймні базово розумієте React, Redux та TypeScript.

Отже, якщо ви не знайомі з цими технологіями, спершу спробуйте прочитати цей практичний посібник з TypeScript або цей посібник React Redux. В іншому випадку давайте почнемо.

Налаштування проекту

Щоб використовувати Redux і TypeScript, нам потрібно створити нову програму React.

Для цього відкриємо CLI (інтерфейс командного рядка) і виконаємо цю команду:

 npx create-react-app my-app --template typescript 

Далі, давайте структуруємо проект наступним чином:

├── src | ├── components | | ├── AddArticle.tsx | | └── Article.tsx | ├── store | | ├── actionCreators.ts | | ├── actionTypes.ts | | └── reducer.ts | ├── type.d.ts | ├── App.test.tsx | ├── App.tsx | ├── index.css | ├── index.tsx | ├── react-app-env.d.ts | └── setupTests.ts ├── tsconfig.json ├── package.json └── yarn.lock 

Файлова структура проекту досить проста. Однак слід зауважити дві речі:

  • storeПапка , яка містить файли , пов'язані з React Redux.
  • type.d.tsФайл , який містить типи машинопису, які можуть бути використані в даний час в інших файлах без імпорту.

Тим не менш, тепер ми можемо встановити Redux і створити наш найперший магазин.

Отже, давайте відкриємо проект і виконаємо таку команду:

 yarn add redux react-redux redux-thunk 

Або при використанні npm

 npm install redux react-redux redux-thunk 

Ми також повинні встановити їх типи як залежності від розробки, щоб допомогти TypeScript зрозуміти бібліотеки.

Отже, давайте виконаємо цю команду ще раз на CLI.

 yarn add -D @types/redux @types/react-redux @types/redux-thunk 

Або для npm:

 npm install -D @types/redux @types/react-redux @types/redux-thunk 

Чудово! З цим кроком вперед ми тепер можемо створювати типи TypeScript для проекту в наступному розділі.

Створіть типи

Типи TypeScript дозволяють встановлювати типи для змінних, параметрів функцій тощо.

  • type.d.ts
interface IArticle { id: number title: string body: string } type ArticleState = { articles: IArticle[] } type ArticleAction = { type: string article: IArticle } type DispatchType = (args: ArticleAction) => ArticleAction 

Тут ми починаємо з оголошення інтерфейсу, IArticleякий відображає форму даної статті.

Тоді ми маємо ArticleState,, ArticleActionі DispatchTypeце буде служити типом, відповідно, для об’єкта стану, творців дій та функції відправки, наданих Redux.

Тим не менш, тепер у нас є необхідні типи, щоб почати використовувати React Redux. Створимо типи дій.

Створіть типи дій

  • store / actionTypes.ts
export const ADD_ARTICLE = "ADD_ARTICLE" export const REMOVE_ARTICLE = "REMOVE_ARTICLE" 

Нам потрібні два типи дій для магазину Redux. Один для додавання статей, а інший для видалення.

Створіть творців дій

  • store / actionCreators.ts
import * as actionTypes from "./actionTypes" export function addArticle(article: IArticle) { const action: ArticleAction = { type: actionTypes.ADD_ARTICLE, article, } return simulateHttpRequest(action) } export function removeArticle(article: IArticle) { const action: ArticleAction = { type: actionTypes.REMOVE_ARTICLE, article, } return simulateHttpRequest(action) } export function simulateHttpRequest(action: ArticleAction) { return (dispatch: DispatchType) => { setTimeout(() => { dispatch(action) }, 500) } } 

У цьому посібнику я змоделюю HTTP-запит, затримавши його на 0,5 секунди. Але сміливо використовуйте справжній сервер, якщо хочете.

Тут функція addArticleнадішле дію щодо додавання нової статті, а метод removeArticleзробить все навпаки. Тож видаліть об’єкт, переданий як аргумент.

Створіть редуктор

Редуктор - це чиста функція, яка приймає стан сховища та дію як параметри, а потім повертає оновлений стан.

  • store / reducer.ts
import * as actionTypes from "./actionTypes" const initialState: ArticleState = { articles: [ { id: 1, title: "post 1", body: "Quisque cursus, metus vitae pharetra Nam libero tempore, cum soluta nobis est eligendi", }, { id: 2, title: "post 2", body: "Harum quidem rerum facilis est et expedita distinctio quas molestias excepturi sint", }, ], } 

Як ви можете бачити тут, ми оголошуємо початковий стан, щоб мати кілька статей, які відображатимуться під час завантаження сторінки. Об'єкт стану повинен відповідати типу ArticleState- інакше TypeScript видасть помилку.

  • store / reducer.ts
const reducer = ( state: ArticleState = initialState, action: ArticleAction ): ArticleState => { switch (action.type) { case actionTypes.ADD_ARTICLE: const newArticle: IArticle = { id: Math.random(), // not really unique title: action.article.title, body: action.article.body, } return { ...state, articles: state.articles.concat(newArticle), } case actionTypes.REMOVE_ARTICLE: const updatedArticles: IArticle[] = state.articles.filter( article => article.id !== action.article.id ) return { ...state, articles: updatedArticles, } } return state } export default reducer 

Next, we have the reducer function that expects the previous state and an action to be able to update the store. Here, we have two actions: one for adding and another for deleting.

With that in place, we can now handle the state with the reducer. Let's now create a store for the project.

Create a store

A Redux store is where your app's state lives.

  • index.tsx
import * as React from "react" import { render } from "react-dom" import { createStore, applyMiddleware, Store } from "redux" import { Provider } from "react-redux" import thunk from "redux-thunk" import App from "./App" import reducer from "./store/reducer" const store: Store & { dispatch: DispatchType } = createStore(reducer, applyMiddleware(thunk)) const rootElement = document.getElementById("root") render(   , rootElement ) 

As you can see, we import the reducer function and then pass it as an argument to the method createStore in order to create a new Redux store. The redux-thunk middleware needs to be proceeded as a second parameter as well to the method to be able to handle asynchronous code.

Next, we connect React to Redux by providing the store object as props to the Provider component.

We can now use Redux in this project and access the store. So, let's create the components to get and manipulate the data.

Create the components

  • components/AddArticle.tsx
import * as React from "react" type Props =  saveArticle: (article: IArticle  export const AddArticle: React.FC = ({ saveArticle }) => { const [article, setArticle] = React.useState() const handleArticleData = (e: React.FormEvent) => { setArticle({ ...article, [e.currentTarget.id]: e.currentTarget.value, }) } const addNewArticle = (e: React.FormEvent) => { e.preventDefault() saveArticle(article) } return (     Add article   ) } 

To add a new article, we will be using this form component. It receives the function saveArticle as a parameter, which allows adding a new article to the store.

The article object should follow the type IArticle to make TypeScript happy.

  • components/Article.tsx
import * as React from "react" import { Dispatch } from "redux" import { useDispatch } from "react-redux" type Props = { article: IArticle removeArticle: (article: IArticle) => void } export const Article: React.FC = ({ article, removeArticle }) => { const dispatch: Dispatch = useDispatch() const deleteArticle = React.useCallback( (article: IArticle) => dispatch(removeArticle(article)), [dispatch, removeArticle] ) return ( 

{article.title}

{article.body}

deleteArticle(article)}>Delete ) }

The Article component shows an article object.

The function removeArticle has to dispatch to access the store and hence delete a given article. That's the reason we use the useDispatch hook here, which lets Redux complete the removing action.

Next, the use of useCallback helps to avoid unnecessary re-rendering by memoizing values as dependencies.

We finally have the components we need to add and show the articles. Let's now add the last piece to the puzzle by using them in the App.tsx file.

  • App.tsx
import * as React from "react" import { useSelector, shallowEqual, useDispatch } from "react-redux" import "./styles.css" import { Article } from "./components/Article" import { AddArticle } from "./components/AddArticle" import { addArticle, removeArticle } from "./store/actionCreators" import { Dispatch } from "redux" const App: React.FC = () => { const articles: readonly IArticle[] = useSelector( (state: ArticleState) => state.articles, shallowEqual ) const dispatch: Dispatch = useDispatch() const saveArticle = React.useCallback( (article: IArticle) => dispatch(addArticle(article)), [dispatch] ) return (  

My Articles

{articles.map((article: IArticle) => ( ))} ) } export default App

The useSelector hook enables access to the state of the store. Here, we pass shallowEqual as a second argument to the method to tell to Redux to use shallow equality when checking for changes.

Next, we rely on useDispatch to dispatch an action for adding articles in the store. Finally, we loop through the array of articles and pass each to the Article component to show it.

With that, we can now browse to the root of the project and then execute this command:

 yarn start 

Or for npm:

 npm start 

If you open //localhost:3000/ in the browser, you should see this:

app-preview

Great! Our app looks good. With this, we have now finished using Redux in a React TypeScript app.

You can find the finished project in this CodeSandbox.

You can find other great content like this on my blog or follow me on Twitter to get notified.

Thanks for reading.