Skip to content

Instantly share code, notes, and snippets.

@arthurproc
Last active May 12, 2024 23:40
Show Gist options
  • Save arthurproc/57ad51b45b4aea60b3aa141c69648673 to your computer and use it in GitHub Desktop.
Save arthurproc/57ad51b45b4aea60b3aa141c69648673 to your computer and use it in GitHub Desktop.
Checklist do Redux - TS + Hooks

Checklist do Redux

  • 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
  • Criar dentro do diretório src/redux:

    • Arquivo index.ts.
    • Diretório actions/.
    • Diretório reducers/.
  • Criar dentro do diretório src/redux/actions/:

    • Arquivo index.ts.
  • Criar dentro do diretório src/redux/reducers/:

    • Arquivo index.ts.
    • Arquivo de cada reducer. Exemplo example.ts.
  • 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ção default.

    • Criar rootReducer usando o combineReducers.

    • 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 do Redux 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
  • 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'));
        };
      
        // ...
      }

Criando Actions Thunk

  • No arquivo de actions src/actions/index.ts:

    • Importar o tipo Dispatch do redux.
    • 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'));
          }
        }
      };
    };
  • 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;
  • 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());
      };
    
      // ...
    }

Tipagem (Opcional)

  • 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);
      
        // ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment