Voltar ao blog
Desenvolvimento
11/02/2026
14 min

React e TypeScript: Melhores Práticas para Desenvolvimento Web Moderno

Domine React com TypeScript: patterns, hooks, performance e arquitetura para criar aplicações web robustas e escaláveis.

E
Equipe Inteligencialy
Autor
React e TypeScript: Melhores Práticas para Desenvolvimento Web Moderno

React consolidou-se como a biblioteca JavaScript mais popular para construção de interfaces de usuário, e TypeScript adicionou type safety que revolucionou a qualidade do código. Juntos, formam uma combinação poderosa para desenvolvimento web moderno.

Por Que React + TypeScript?

Benefícios do TypeScript

  1. Type Safety: Menos bugs em produção
  2. IntelliSense: Autocomplete e documentação inline
  3. Refactoring Seguro: Mudanças com confiança
  4. Documentação Viva: Tipos servem como documentação
  5. Escalabilidade: Código mais maintainable

Configuração Inicial

# Criar novo projeto com Vite
npm create vite@latest my-app -- --template react-ts

# Ou com Next.js
npx create-next-app@latest --typescript

# Ou adicionar TypeScript a projeto existente
npm install --save-dev typescript @types/react @types/react-dom
npx tsc --init

Componentes com TypeScript

Functional Components

// Componente simples
interface ButtonProps {
  label: string;
  onClick: () => void;
  variant?: 'primary' | 'secondary';
  disabled?: boolean;
}

export const Button: React.FC<ButtonProps> = ({ 
  label, 
  onClick, 
  variant = 'primary',
  disabled = false 
}) => {
  return (
    <button 
      onClick={onClick}
      disabled={disabled}
      className={`btn btn-${variant}`}
    >
      {label}
    </button>
  );
};

Props com Children

interface CardProps {
  title: string;
  children: React.ReactNode;
  footer?: React.ReactNode;
}

export const Card: React.FC<CardProps> = ({ title, children, footer }) => {
  return (
    <div className="card">
      <div className="card-header">{title}</div>
      <div className="card-body">{children}</div>
      {footer && <div className="card-footer">{footer}</div>}
    </div>
  );
};

Event Handlers

interface FormProps {
  onSubmit: (data: FormData) => void;
}

export const Form: React.FC<FormProps> = ({ onSubmit }) => {
  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    onSubmit(formData);
  };

  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    console.log(e.target.value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input onChange={handleInputChange} />
    </form>
  );
};

Hooks com TypeScript

useState

// Tipo inferido
const [count, setCount] = useState(0);

// Tipo explícito
const [user, setUser] = useState<User | null>(null);

// Com interface
interface User {
  id: number;
  name: string;
  email: string;
}

const [users, setUsers] = useState<User[]>([]);

useEffect

useEffect(() => {
  const fetchData = async () => {
    const response = await fetch('/api/users');
    const data: User[] = await response.json();
    setUsers(data);
  };

  fetchData();
}, []); // Dependências tipadas automaticamente

useContext

interface AuthContextType {
  user: User | null;
  login: (email: string, password: string) => Promise<void>;
  logout: () => void;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

export const useAuth = () => {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within AuthProvider');
  }
  return context;
};

Custom Hooks

interface UseFetchResult<T> {
  data: T | null;
  loading: boolean;
  error: Error | null;
  refetch: () => void;
}

function useFetch<T>(url: string): UseFetchResult<T> {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  const fetchData = async () => {
    try {
      setLoading(true);
      const response = await fetch(url);
      const json = await response.json();
      setData(json);
    } catch (err) {
      setError(err as Error);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchData();
  }, [url]);

  return { data, loading, error, refetch: fetchData };
}

// Uso
const { data, loading } = useFetch<User[]>('/api/users');

Patterns Avançados

Compound Components

interface TabsProps {
  defaultValue: string;
  children: React.ReactNode;
}

interface TabsContextType {
  activeTab: string;
  setActiveTab: (tab: string) => void;
}

const TabsContext = createContext<TabsContextType | undefined>(undefined);

export const Tabs: React.FC<TabsProps> & {
  List: typeof TabsList;
  Tab: typeof Tab;
  Panel: typeof TabPanel;
} = ({ defaultValue, children }) => {
  const [activeTab, setActiveTab] = useState(defaultValue);

  return (
    <TabsContext.Provider value={{ activeTab, setActiveTab }}>
      <div className="tabs">{children}</div>
    </TabsContext.Provider>
  );
};

Tabs.List = TabsList;
Tabs.Tab = Tab;
Tabs.Panel = TabPanel;

Render Props

interface DataFetcherProps<T> {
  url: string;
  render: (data: T, loading: boolean, error: Error | null) => React.ReactNode;
}

function DataFetcher<T>({ url, render }: DataFetcherProps<T>) {
  const { data, loading, error } = useFetch<T>(url);
  return <>{render(data, loading, error)}</>;
}

// Uso
<DataFetcher<User[]>
  url="/api/users"
  render={(users, loading, error) => (
    loading ? <Loading /> : <UserList users={users} />
  )}
/>

Higher-Order Components (HOC)

function withAuth<P extends object>(
  Component: React.ComponentType<P>
): React.FC<P> {
  return (props) => {
    const { user } = useAuth();
    
    if (!user) {
      return <Navigate to="/login" />;
    }
    
    return <Component {...props} />;
  };
}

// Uso
const ProtectedDashboard = withAuth(Dashboard);

Performance Optimization

React.memo

interface TodoItemProps {
  todo: Todo;
  onToggle: (id: number) => void;
}

export const TodoItem = React.memo<TodoItemProps>(
  ({ todo, onToggle }) => {
    return (
      <div onClick={() => onToggle(todo.id)}>
        {todo.title}
      </div>
    );
  },
  (prevProps, nextProps) => {
    // Custom comparison
    return prevProps.todo.id === nextProps.todo.id;
  }
);

useCallback

const TodoList: React.FC = () => {
  const [todos, setTodos] = useState<Todo[]>([]);

  const handleToggle = useCallback((id: number) => {
    setTodos(prev => prev.map(todo => 
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    ));
  }, []);

  return (
    <div>
      {todos.map(todo => (
        <TodoItem key={todo.id} todo={todo} onToggle={handleToggle} />
      ))}
    </div>
  );
};

useMemo

const Dashboard: React.FC<{ data: number[] }> = ({ data }) => {
  const statistics = useMemo(() => {
    return {
      total: data.reduce((sum, val) => sum + val, 0),
      average: data.length > 0 ? data.reduce((sum, val) => sum + val, 0) / data.length : 0,
      max: Math.max(...data),
      min: Math.min(...data),
    };
  }, [data]);

  return <div>Total: {statistics.total}</div>;
};

State Management

Zustand com TypeScript

import create from 'zustand';

interface Todo {
  id: number;
  title: string;
  completed: boolean;
}

interface TodoStore {
  todos: Todo[];
  addTodo: (title: string) => void;
  toggleTodo: (id: number) => void;
  removeTodo: (id: number) => void;
}

export const useTodoStore = create<TodoStore>((set) => ({
  todos: [],
  addTodo: (title) => set((state) => ({
    todos: [...state.todos, { id: Date.now(), title, completed: false }]
  })),
  toggleTodo: (id) => set((state) => ({
    todos: state.todos.map(todo =>
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    )
  })),
  removeTodo: (id) => set((state) => ({
    todos: state.todos.filter(todo => todo.id !== id)
  })),
}));

Testing

Jest + React Testing Library

import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from './Button';

describe('Button', () => {
  it('should render with label', () => {
    render(<Button label="Click me" onClick={() => {}} />);
    expect(screen.getByText('Click me')).toBeInTheDocument();
  });

  it('should call onClick when clicked', () => {
    const handleClick = jest.fn();
    render(<Button label="Click" onClick={handleClick} />);
    fireEvent.click(screen.getByText('Click'));
    expect(handleClick).toHaveBeenCalledTimes(1);
  });

  it('should be disabled when prop is true', () => {
    render(<Button label="Click" onClick={() => {}} disabled />);
    expect(screen.getByRole('button')).toBeDisabled();
  });
});

Conclusão

React + TypeScript é a combinação perfeita para desenvolvimento web moderno. Com type safety, melhor DX e código mais maintainable, você estará preparado para criar aplicações escaláveis e robustas.

Precisa de ajuda com seu projeto React? A Inteligencialy tem expertise em React, TypeScript e arquiteturas escaláveis. Entre em contato!

Tags

ReactTypeScriptFrontendJavaScript