npx create-react-app my-app --template typescript
A seguir, vamos estruturar o projeto da seguinte maneira:
├── 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
A estrutura de arquivos do projeto é bastante simples. No entanto, há duas coisas a serem observadas:
- o
store
pasta que contém arquivos relacionados ao React Redux. - o
type.d.ts
arquivo que contém os tipos TypeScript, que podem ser usados agora em outros arquivos sem importação.
Dito isso, agora podemos instalar o Redux e criar nossa primeira loja.
Então, vamos abrir o projeto e executar o seguinte comando:
yarn add redux react-redux redux-thunk
Ou ao usar npm
npm install redux react-redux redux-thunk
Também temos que instalar seus tipos como dependências de desenvolvimento para ajudar o TypeScript a entender as bibliotecas.
Então, vamos executar este comando novamente na CLI.
yarn add -D @types/redux @types/react-redux @types/redux-thunk
Ou para npm
:
npm install -D @types/redux @types/react-redux @types/redux-thunk
Ótimo! Com este passo adiante, podemos agora criar os tipos TypeScript para o projeto na próxima seção.
Crie os tipos
Os tipos de TypeScript permitem definir tipos para suas variáveis, parâmetros de função e assim por diante.
interface IArticle { id: number title: string body: string}type ArticleState = { articles: IArticle[]}type ArticleAction = { type: string article: IArticle}type DispatchType = (args: ArticleAction) => ArticleAction
Aqui, começamos declarando a interface IArticle
que reflete a forma de um determinado artigo.
Então nós temos ArticleState
, ArticleAction
, e DispatchType
que servirão como tipos para, respectivamente, o objeto de estado, os criadores de ação e a função de despacho fornecida por Redux.
Dito isso, agora temos os tipos necessários para começar a usar o React Redux. Vamos criar os tipos de ação.
Crie os tipos de ação
export const ADD_ARTICLE = "ADD_ARTICLE"export const REMOVE_ARTICLE = "REMOVE_ARTICLE"
Precisamos de dois tipos de ação para a loja Redux. Um para adicionar artigos e outro para excluir.
Crie os criadores de ação
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) }}
Neste tutorial, simularei a solicitação HTTP atrasando-a por 0,5 segundos. Mas fique à vontade para usar um servidor real se quiser.
Aqui, a função addArticle
irá despachar uma ação para adicionar um novo artigo, e o método removeArticle
fará o oposto. Portanto, exclua o objeto passado como argumento.
Crie um redutor
Um redutor é uma função pura que recebe o estado da loja e uma ação como parâmetros e então retorna o estado atualizado.
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", }, ],}
Como você pode ver aqui, declaramos um estado inicial para ter alguns artigos para mostrar quando a página carregar. O objeto de estado precisa corresponder ao tipo ArticleState
– caso contrário, o TypeScript gerará um erro.
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
Em seguida, temos o reducer
função que espera o estado anterior e uma ação para poder atualizar a loja. Aqui, temos duas ações: uma para adicionar e outra para excluir.
Com isso no lugar, agora podemos lidar com o estado com o redutor. Vamos agora criar uma loja para o projeto.
Crie uma loja
Uma loja Redux é onde mora o estado do seu aplicativo.
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)
Como você pode ver, importamos a função redutora e a passamos como um argumento para o método createStore
para criar uma nova loja Redux. o redux-thunk
O middleware precisa ser seguido como um segundo parâmetro, bem como o método para ser capaz de lidar com o código assíncrono.
Em seguida, conectamos o React ao Redux, fornecendo o store
objeto como adereços para o Provider
componente.
Agora podemos usar Redux neste projeto e acessar a loja. Portanto, vamos criar os componentes para obter e manipular os dados.
Crie os componentes
- componentes / 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> )}
Para adicionar um novo artigo, usaremos este componente de formulário. Recebe a função saveArticle
como parâmetro, que permite adicionar um novo artigo à loja.
O objeto do artigo deve seguir o tipo IArticle
para deixar o TypeScript feliz.
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> )}
o Article
componente mostra um objeto de artigo.
A função removeArticle
tem que despachar para acessar a loja e, portanto, excluir um determinado artigo. Essa é a razão pela qual usamos o useDispatch
gancho aqui, que permite Redux completar a ação de remoção.
Em seguida, o uso de useCallback
ajuda a evitar uma nova renderização desnecessária, memorizando os valores como dependências.
Finalmente temos os componentes de que precisamos para adicionar e mostrar os artigos. Vamos agora adicionar a última peça ao quebra-cabeça usando-as no App.tsx
Arquivo.
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
o useSelector
gancho permite o acesso ao estado da loja. Aqui passamos shallowEqual
como um segundo argumento para o método para dizer ao Redux para usar igualdade superficial ao verificar as alterações.
Em seguida, contamos com useDispatch
para despachar uma ação para adicionar artigos na loja. Por fim, percorremos a matriz de artigos e passamos cada um para o Article
componente para mostrá-lo.
Com isso, agora podemos navegar até a raiz do projeto e, em seguida, executar este comando:
yarn start
Ou para npm
:
npm start
Se você abrir http://localhost:3000/
no navegador, você verá o seguinte:
Ótimo! Nosso aplicativo parece bom. Com isso, terminamos de usar o Redux em um aplicativo React TypeScript.
Você pode encontrar o projeto concluído neste CodeSandbox.
Você pode encontrar outro ótimo conteúdo como este em meu blog ou me segue no Twitter para ser notificado.
Obrigado pela leitura.