Introdução

Neste tutorial, vamos explorar o desenvolvimento de uma aplicação de lista de tarefas utilizando TypeScript, React e Redux. A aplicação permitirá que os usuários adicionem, removam e visualizem tarefas, seguindo as melhores práticas de desenvolvimento, incluindo uso de tipagem estática com TypeScript, gerenciamento de estado global com Redux e testes automatizados com Jest. Ao final deste guia, você terá uma aplicação funcional e testada, além de uma compreensão sólida dos padrões usados na construção de aplicações modernas em JavaScript.

Etapas

  1. Configuração do Ambiente de Desenvolvimento

    Antes de começar a codificar, precisamos configurar nosso ambiente de desenvolvimento. Certifique-se de ter o Node.js e o npm (Node Package Manager) instalados. Você pode verificar as versões instaladas usando os comandos `node -v` e `npm -v`. Depois, crie um novo projeto utilizando o Create React App com suporte a TypeScript.

    commands
    # Verificar versões instaladas
    node -v
    npm -v
    # Criar um novo projeto React com TypeScript
    npx create-react-app my-todo-app --template typescript
    # Navegar até o diretório do projeto
    cd my-todo-app

  2. Instalação das Dependências Redux

    Para gerenciar o estado da aplicação, vamos utilizar o Redux. Instale as seguintes bibliotecas: redux, react-redux e @reduxjs/toolkit para facilitar a criação da store e dos slices.

    commands
    # Instalar o Redux e dependências
    npm install redux react-redux @reduxjs/toolkit

  3. Configuração da Store Redux

    Crie uma pasta chamada `store` dentro do diretório `src`. Dentro dessa pasta, crie o arquivo `store.ts`, onde vamos configurar nossa store Redux.

    store/store.ts
    import { configureStore } from '@reduxjs/toolkit';
    import taskReducer from './taskSlice';
    
    const store = configureStore({
      reducer: {
        tasks: taskReducer
      }
    });
    
    export default store;

  4. Criação do Slice de Tarefas

    Crie um arquivo chamado `taskSlice.ts` na mesma pasta `store`. Nesse arquivo, definiremos as ações e o estado inicial das nossas tarefas.

    store/taskSlice.ts
    import { createSlice, PayloadAction } from '@reduxjs/toolkit';
    
    interface Task {
      id: number;
      title: string;
      completed: boolean;
    }
    
    interface TaskState {
      tasks: Task[];
    }
    
    const initialState: TaskState = {
      tasks: [],
    };
    
    const taskSlice = createSlice({
      name: 'tasks',
      initialState,
      reducers: {
        addTask(state, action: PayloadAction<string>) {
          const newTask: Task = {
            id: Date.now(),
            title: action.payload,
            completed: false,
          };
          state.tasks.push(newTask);
        },
        removeTask(state, action: PayloadAction<number>) {
          state.tasks = state.tasks.filter(task => task.id !== action.payload);
        },
        toggleTask(state, action: PayloadAction<number>) {
          const task = state.tasks.find(task => task.id === action.payload);
          if (task) {
            task.completed = !task.completed;
          }
        }
      },
    });
    
    export const { addTask, removeTask, toggleTask } = taskSlice.actions;
    export default taskSlice.reducer;

  5. Criação do Componente de Lista de Tarefas

    Dentro do diretório `src`, crie uma pasta chamada `components` e adicione um arquivo `TaskList.tsx`. Este componente renderizará a lista de tarefas e permitirá adicionar novas tarefas.

    components/TaskList.tsx
    import React, { useState } from 'react';
    import { useDispatch, useSelector } from 'react-redux';
    import { addTask, removeTask, toggleTask } from '../store/taskSlice';
    
    const TaskList: React.FC = () => {
      const [taskInput, setTaskInput] = useState('');
      const dispatch = useDispatch();
      const tasks = useSelector((state: any) => state.tasks.tasks);
    
      const handleAddTask = () => {
        if (taskInput) {
          dispatch(addTask(taskInput));
          setTaskInput('');
        }
      };
    
      return (
        <div>
          <h2>Lista de Tarefas</h2>
          <input
            type="text"
            value={taskInput}
            onChange={e => setTaskInput(e.target.value)}
            placeholder="Adicionar nova tarefa"
          />
          <button onClick={handleAddTask}>Adicionar</button>
          <ul>
            {tasks.map(task => (
              <li key={task.id} onClick={() => dispatch(toggleTask(task.id))} style={{ textDecoration: task.completed ? 'line-through' : 'none' }}>
                {task.title}
                <button onClick={() => dispatch(removeTask(task.id))}>Remover</button>
              </li>
            ))}
          </ul>
        </div>
      );
    };
    
    export default TaskList;

  6. Integração do Redux no Componente Principal

    No arquivo `index.tsx`, vamos integrar a store Redux com o React utilizando o Provider do react-redux.

    index.tsx
    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import { Provider } from 'react-redux';
    import store from './store/store';
    import App from './App';
    
    const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
    root.render(
      <Provider store={store}>
        <App />
      </Provider>
    );

  7. Criação do Componente Principal

    Agora, edite o arquivo `App.tsx` para incluir o componente `TaskList` que criamos anteriormente.

    App.tsx
    import React from 'react';
    import TaskList from './components/TaskList';
    
    const App: React.FC = () => {
      return (
        <div>
          <h1>Aplicação de Lista de Tarefas</h1>
          <TaskList />
        </div>
      );
    };
    
    export default App;

  8. Implementação de Testes Automatizados com Jest

    Crie um arquivo `taskSlice.test.ts` na pasta `store`. Neste arquivo, vamos implementar testes para nossas funções do taskSlice utilizando Jest.

    store/taskSlice.test.ts
    import taskReducer, { addTask, removeTask, toggleTask } from './taskSlice';
    
    describe('taskSlice', () => {
      it('deve adicionar uma nova tarefa', () => {
        const initialState = { tasks: [] };
        const newState = taskReducer(initialState, addTask('Nova Tarefa'));
        expect(newState.tasks).toHaveLength(1);
        expect(newState.tasks[0].title).toBe('Nova Tarefa');
      });
    
      it('deve remover uma tarefa', () => {
        const initialState = { tasks: [{ id: 1, title: 'Tarefa 1', completed: false }] };
        const newState = taskReducer(initialState, removeTask(1));
        expect(newState.tasks).toHaveLength(0);
      });
    
      it('deve alternar o status de uma tarefa', () => {
        const initialState = { tasks: [{ id: 1, title: 'Tarefa 1', completed: false }] };
        const newState = taskReducer(initialState, toggleTask(1));
        expect(newState.tasks[0].completed).toBe(true);
      });
    });

  9. Executando a Aplicação e os Testes

    Para executar a aplicação, utilize o comando `npm start`. Para executar os testes, utilize o comando `npm test` e selecione as opções apropriadas para rodar os testes.

    commands
    # Executar a aplicação
    npm start
    # Executar os testes
    npm test

Conclusão

Neste tutorial, você aprendeu a criar uma aplicação de lista de tarefas utilizando TypeScript, React e Redux, implementando recursos essenciais como adição, remoção e alteração do estado das tarefas. Você também integrou testes automatizados com Jest, garantindo que a lógica da sua aplicação esteja funcionando conforme esperado. Agora você pode expandir essa base, adicionando mais funcionalidades e refinando o design da sua aplicação.

Hashtags

#TypeScript #React #Redux #Jest #DesenvolvimentoWeb #TestesAutomatizados