-
Antes de começar
- pensar como será o formato do seu estado global
- pensar quais actions serão necessárias na sua aplicação
-
Instalação
-
npm i redux react-redux @redux-devtools/extension
-
-
Criar diretórios do Redux:
- diretório
src/redux
- diretório
-
Criar dentro do diretório
src/redux
:- Arquivo
index.ts
. - Diretório
actions/
. - Diretório
reducers/
.
- Arquivo
-
Criar dentro do diretório
src/redux/actions/
:- Arquivo
index.ts
.
- Arquivo
-
Criar dentro do diretório
src/redux/reducers/
:- Arquivo
index.ts
. - Arquivo de cada reducer. Exemplo
example.ts
.
- Arquivo
-
Criar dentro do arquivo
src/redux/index.ts
:-
Importar o
createStore
. -
Configurar o Redux DevTools.
-
Importar o
rootReducer
. -
Criar e exportar a
store
.Exemplo:
import { legacy_createStore as createStore } from 'redux'; import { composeWithDevTools } from '@redux-devtools/extension'; import rootReducer from './reducers'; const store = createStore(rootReducer, composeWithDevTools()); export default store;
-
-
Criar dentro do arquivo
src/redux/reducers/index.ts
:-
Criar reducer em arquivo específico para ele. Exemplo:
src/redux/reducers/example.ts
-
Criar Estado inicial.
-
Criar função reducer com
switch
retornando apenas a opçãodefault
. -
Criar
rootReducer
usando ocombineReducers
. -
Exportar
rootReducer
.Exemplo:
// src/redux/reducers/example.ts // Arquivo para o reducer de nome exampleReducer import { AnyAction } from 'redux'; const INITIAL_STATE = { // Este é um estado inicial de exemplo // Modifique para o seu estado email: '' }; const exampleReducer = ( state = INITIAL_STATE, action: AnyAction, ) => { switch (action.type) { default: return state; } }; export default exampleReducer;
// src/redux/reducers/index.ts import { combineReducers } from 'redux'; import exampleReducer from './example'; const rootReducer = combineReducers({ // Este é um nome de reducer de exemplo // Modifique para o seu uso example: exampleReducer, }); export default rootReducer;
-
-
No arquivo
src/main.ts
:-
Importar a
store
. -
Importar o
Provider
, para fornecer os estados a todos os componentes encapsulados pelo<App />
.Exemplo:
// Na importação import { Provider } from 'react-redux'; import store from './redux';
// No render <Provider store={ store } > <App /> </Provider>
-
Importante
⚠️ : Aqui o redux já deve estar configurado e funcionando. Utilize a extensão doRedux Devtools
para verificar se o seu estado aparece lá normalmente. Caso não apareça, volte e verifique se todos os passos foram executados corretamente.
Agora vamos seguir agora para adicionar actions e permitir uma modificação no estado.
- No arquivo
src/redux/actions/index.ts
:-
Criar e exportar Action Types e Action Creators.
Exemplo:
// src/redux/actions/index.ts // Action Types export const ADD_EMAIL = 'ADD_EMAIL'; // Action Creators export const addEmail = (email) => ({ type: ADD_EMAIL, payload: email, })
-
Criar base no reducer para tratar a action criada.
-
A estrutura final de arquivos ficaria assim:
-
src/
- redux/
- index.ts
- actions/
- index.ts
- reducers/
- index.ts
- example.ts
- redux/
-
Nos reducers:
-
Importar as Action Types criadas
-
Criar os casos para cada action criada, retornando o devido estado atualizado.
// src/redux/reducers/example.ts // Arquivo para o reducer de nome exampleReducer import { AnyAction } from 'redux'; import { ADD_EMAIL } from '../actions'; const INITIAL_STATE = { // Este é um estado inicial de exemplo // Modifique para o seu estado email: '' }; const exampleReducer = ( state = INITIAL_STATE, action: AnyAction, ) => { switch (action.type) { case ADD_EMAIL: { return { ...state, email: action.payload, } } default: return state; } }; export default exampleReducer;
-
-
Nos componentes que irão ler o estado:
-
Importar o hook
useSelector
. -
Buscar os estado que precisamos acessar
useSelector
.// No import import { useSelector } from 'react-redux'; function MyComponent() { // Acesso ao estado global // Dentro da função do componente const { email } = useSelector((state) => state.example); // ...
-
-
Nos componentes que irão modificar o estado:
-
Importar a action creator a ser utilizada.
-
Importar o hook
useDispatch
. -
Pegar a função dispatch do hook.
-
Utilizar a função dispatch para enviar a action ao reducer.
// No import import { addEmail } from '../redux/actions'; import { useDispatch } from 'react-redux';
function MyComponent() { // Buscando função dispatch // Dentro do código do componente const dispatch = useDispatch(); // Disparando a action const handleClick = () => { dispatch(addEmail('teste@teste.com')); }; // ... }
-
-
No arquivo de actions
src/actions/index.ts
:- Importar o tipo
Dispatch
doredux
. - Criar actions Types das actions intermediárias.
- Criar action creators intermediárias.
- Criar função da Action Thunk.
Exemplo:
// No import import { Dispatch } from 'redux'; // Action Types export const REQUEST_USERNAME = 'REQUEST_USERNAME'; export const REQUEST_USERNAME_SUCCESS = 'REQUEST_USERNAME_SUCCESS'; export const REQUEST_USERNAME_ERROR = 'REQUEST_USERNAME_ERROR'; // Iniciando a requisição const requestUsername = () => ({ type: REQUEST_USERNAME, }); // Requisição foi um sucesso const requestUsernameSuccess = (username: string) => ({ type: REQUEST_USER_SUCCESS, payload: { username, }, }); // Ocorreu algum erro na requisição const requestUsernameError = (errorMessage: string) => ({ type: REQUEST_USER_ERROR, payload: { errorMessage } }); // Action Thunk export const fetchUsername = () => { return async (dispatch: Dispatch) => { try { dispatch(requestUsername()); const response = await fetch(API_URL); const user = await response.json(); dispatch(requestUsernameSuccess(user)); } catch (error) { if (error instanceof Error) { dispatch(requestUsernameError(error.message)); } else { dispatch(requestUsernameError('Erro desconhecido ao buscar nome de usuário')); } } }; };
- Importar o tipo
-
No arquivo do reducer
src/reducers/example.ts
:- Importar as novas
actions
criadas - Criar um case para cada uma das
actions
adicionadas. - Dentro dos cases, modificar o estado de acordo com a ação.
Exemplo:
// No import import { ADD_EMAIL, REQUEST_USERNAME, REQUEST_USERNAME_ERROR, REQUEST_USERNAME_SUCCESS, } from '../actions'; const exampleReducer = ( state = INITIAL_STATE, action: AnyAction, ) => { switch (action.type) { case ADD_EMAIL: { return { ...state, email: action.payload, } } case REQUEST_USERNAME: { return { ...state, isLoading: true, }; } case REQUEST_USERNAME_SUCCESS: { return { ...state, isLoading: false, username: action.payload.username, errorMessage: '', }; } case REQUEST_USERNAME_ERROR: { return { ...state, isLoading: false, errorMessage: action.payload.errorMessage, }; } default: return state; } }; export default exampleReducer;
- Importar as novas
-
Nos componentes que vão disparar a
action thunk
:- Importar a
action thunk
fetchUsername
- Disparar ela normalmente (como qualquer outra
action
)
Exemplo:
// No import import { fetchUsername } from '../redux/actions'; function MyComponent() { const dispatch = useDispatch(); // Disparando a action thunk const handleSubmit = () => { dispatch(fetchUsername()); }; // ... }
- Importar a
-
No arquivo
src/types.ts
-
Criar os tipos para cada estado dos reducers
-
Criar o tipo para o estado global
-
Criar tipagem do Dispatch com thunk
Exemplo:
// no import import { ThunkDispatch } from 'redux-thunk'; import { AnyAction } from 'redux'; export type ExampleReducerState = { email: string; }; export type ReduxState = { example: ExampleReducerState; }; export type AppDispatch = ThunkDispatch<RootState, unknown, AnyAction>;
-
-
No arquivo do reducer
src/redux/reducers/example.ts
-
Adicionar o tipo no argumento do reducer
Exemplo:
// No import import { ExampleReducerState } from '../../types'; // No argumento do reducer const exampleReducer = ( state: ExampleReducerState = INITIAL_STATE, action: AnyAction, ) => {
-
-
Nos componentes que leem do estado
-
Adicionar o tipo no
useSelector
.Exemplo:
// No import import { ReduxState } from '../../types'; function MyComponent() { // Acesso ao estado global // Dentro da função do componente const { email } = useSelector((state: ReduxState) => state.example); // ...
-