Redux este un container de stare previzibil pentru aplicațiile JavaScript. Este o bibliotecă populară pentru gestionarea stării în aplicațiile React.

Redux vă poate oferi o experiență de dezvoltator mai bună atunci când o utilizați împreună cu TypeScript. TypeScript este un superset de JavaScript care verifică tipul codului pentru ao face robust și ușor de înțeles.

În acest ghid, vă voi arăta cum să utilizați Redux în proiectul dvs. React TypeScript prin construirea unei aplicații care vă permite să adăugați, să ștergeți și să afișați articole.

Hai să ne scufundăm.

  • Condiții prealabile
  • Configurare
  • Creați tipurile
  • Creați tipurile de acțiune
  • Creați creatorii de acțiuni
  • Creați un reductor
  • Creați un magazin
  • Creați componentele

Condiții prealabile

Acest tutorial presupune că aveți cel puțin o înțelegere de bază despre React, Redux și TypeScript.

Deci, dacă nu sunteți familiarizați cu aceste tehnologii, încercați mai întâi să citiți acest lucru ghid practic pentru TypeScript sau acest tutorial React Redux. În caz contrar, să începem.

Configurarea proiectului

Pentru a utiliza Redux și TypeScript, trebuie să creăm o nouă aplicație React.

Pentru a face acest lucru, să deschidem CLI (interfața liniei de comandă) și să executăm această comandă:

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

În continuare, să structurăm proiectul după cum urmează:

├── 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

Structura fișierelor proiectului este destul de simplă. Cu toate acestea, există două lucruri de remarcat:

  • store folder care conține fișiere legate de React Redux.
  • type.d.ts fișier care conține tipurile TypeScript, care poate fi utilizat acum în alte fișiere fără import.

Acestea fiind spuse, acum putem instala Redux și putem crea primul nostru magazin.

Deci, să deschidem proiectul și să executăm următoarea comandă:

  yarn add redux react-redux redux-thunk

Sau atunci când utilizați npm

  npm install redux react-redux redux-thunk

De asemenea, trebuie să le instalăm tipurile ca dependențe de dezvoltare pentru a ajuta TypeScript să înțeleagă bibliotecile.

Deci, haideți să executăm această comandă din nou pe CLI.

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

Sau pentru npm:

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

Grozav! Cu acest pas înainte, putem crea acum tipurile TypeScript pentru proiect în secțiunea următoare.

Creați tipurile

Tipurile TypeScript vă permit să setați tipuri pentru variabilele, parametrii funcției și așa mai departe.

  • tip.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

Aici, începem prin declararea interfeței IArticle care reflectă forma unui articol dat.

Atunci noi avem ArticleState, ArticleAction, și DispatchType care va servi ca tipuri pentru obiectul de stat, respectiv pentru creatorii de acțiuni și funcția de expediere oferită de Redux.

Acestea fiind spuse, avem acum tipurile necesare pentru a începe să folosim React Redux. Să creăm tipurile de acțiune.

Creați tipurile de acțiune

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

Avem nevoie de două tipuri de acțiuni pentru magazinul Redux. Una pentru adăugarea articolelor și alta pentru ștergere.

Creați creatorii de acțiuni

  • 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)
  }
}

În acest tutorial, voi simula cererea HTTP întârziind-o cu 0,5 secunde. Dar nu ezitați să utilizați un server real dacă doriți.

Aici, funcția addArticle va trimite o acțiune pentru adăugarea unui nou articol și metoda removeArticle va face contrariul. Deci, ștergeți obiectul transmis ca argument.

Creați un reductor

Un reductor este o funcție pură care primește starea magazinului și o acțiune ca parametri și apoi returnează starea actualizată.

  • magazin / reductor.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",
    },
  ],
}

După cum puteți vedea aici, declarăm o stare inițială pentru a avea câteva articole de afișat la încărcarea paginii. Obiectul de stare trebuie să se potrivească cu tipul ArticleState – în caz contrar, TypeScript va genera o eroare.

  • magazin / reductor.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

Apoi, avem reducer funcție care așteaptă starea precedentă și o acțiune pentru a putea actualiza magazinul. Aici avem două acțiuni: una pentru adăugare și alta pentru ștergere.

Având în vedere acest lucru, acum putem gestiona starea cu reductorul. Să creăm acum un magazin pentru proiect.

Creați un magazin

Un magazin Redux este locul în care trăiește starea aplicației dvs.

  • 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<ArticleState, ArticleAction> & {
  dispatch: DispatchType
} = createStore(reducer, applyMiddleware(thunk))

const rootElement = document.getElementById("root")
render(
  <Provider store={store}>
    <App />
  </Provider>,
  rootElement
)

După cum puteți vedea, importăm funcția de reducere și apoi o transmitem ca argument către metodă createStore pentru a crea un nou magazin Redux. redux-thunk middleware trebuie să fie trecut ca un al doilea parametru, de asemenea, la metoda pentru a putea gestiona codul asincron.

Apoi, conectăm React la Redux furnizând fișierul store obiect ca recuzită pentru Provider componentă.

Acum putem folosi Redux în acest proiect și putem accesa magazinul. Deci, să creăm componentele pentru a obține și manipula datele.

Creați componentele

  • components / AddArticle.tsx
import * as React from "react"

type Props = {
  saveArticle: (article: IArticle | any) => void
}

export const AddArticle: React.FC<Props> = ({ saveArticle }) => {
  const [article, setArticle] = React.useState<IArticle | {}>()

  const handleArticleData = (e: React.FormEvent<HTMLInputElement>) => {
    setArticle({
      ...article,
      [e.currentTarget.id]: e.currentTarget.value,
    })
  }

  const addNewArticle = (e: React.FormEvent) => {
    e.preventDefault()
    saveArticle(article)
  }

  return (
    <form onSubmit={addNewArticle} className="Add-article">
      <input
        type="text"
        id="title"
        placeholder="Title"
        onChange={handleArticleData}
      />
      <input
        type="text"
        id="body"
        placeholder="Description"
        onChange={handleArticleData}
      />
      <button disabled={article === undefined ? true : false}>
        Add article
      </button>
    </form>
  )
}

Pentru a adăuga un articol nou, vom folosi această componentă de formular. Primește funcția saveArticle ca parametru, care permite adăugarea unui articol nou în magazin.

Obiectul articolului ar trebui să urmeze tipul IArticle pentru a face TypeScript fericit.

  • 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<Props> = ({ article, removeArticle }) => {
  const dispatch: Dispatch<any> = useDispatch()

  const deleteArticle = React.useCallback(
    (article: IArticle) => dispatch(removeArticle(article)),
    [dispatch, removeArticle]
  )

  return (
    <div className="Article">
      <div>
        <h1>{article.title}</h1>
        <p>{article.body}</p>
      </div>
      <button onClick={() => deleteArticle(article)}>Delete</button>
    </div>
  )
}

Article componentă arată un obiect articol.

Functia removeArticle trebuie să trimită pentru a accesa magazinul și, prin urmare, să șteargă un anumit articol. Acesta este motivul pentru care folosim useDispatch cârlig aici, care permite Redux să finalizeze acțiunea de eliminare.

Apoi, utilizarea useCallback ajută la evitarea redării inutile prin memorarea valorilor ca dependențe.

În sfârșit avem componentele de care trebuie să adăugăm și să afișăm articolele. Să adăugăm acum ultima piesă la puzzle folosindu-le în App.tsx fişier.

  • 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<any> = useDispatch()

  const saveArticle = React.useCallback(
    (article: IArticle) => dispatch(addArticle(article)),
    [dispatch]
  )

  return (
    <main>
      <h1>My Articles</h1>
      <AddArticle saveArticle={saveArticle} />
      {articles.map((article: IArticle) => (
        <Article
          key={article.id}
          article={article}
          removeArticle={removeArticle}
        />
      ))}
    </main>
  )
}

export default App

useSelector hook permite accesul la starea magazinului. Aici, trecem shallowEqual ca un al doilea argument pentru metoda de a spune Redux să utilizeze egalitate superficială atunci când verifică modificările.

Apoi, ne bazăm pe useDispatch pentru a trimite o acțiune pentru adăugarea articolelor în magazin. În cele din urmă, vom parcurge matricea de articole și le vom trece la Article componentă pentru ao arăta.

Cu aceasta, putem naviga acum la rădăcina proiectului și apoi putem executa această comandă:

  yarn start

Sau pentru npm:

  npm start

Dacă deschizi http://localhost:3000/ în browser, ar trebui să vedeți acest lucru:

previzualizare aplicație

Grozav! Aplicația noastră arată bine. Cu aceasta, acum am terminat de utilizat Redux într-o aplicație React TypeScript.

Puteți găsi proiectul finalizat în această CodeSandbox.

Puteți găsi alt conținut excelent ca acesta pe blogul meu sau urmează-mă pe Twitter pentru a fi notificat.

Mulțumesc pentru lectură.