Skip to content

Página (Screen)

A Página ou Screen é o componente de mais alto nível na nossa hierarquia de visualização. Ela é responsável por orquestrar a interface, capturar as intenções do usuário e exibir os dados processados pelas camadas inferiores.


1. Estrutura e Responsabilidade

Uma tela deve ser composta por dois arquivos principais:

  1. *.screen.tsx: Contém apenas a estrutura visual (JSX) e o mapeamento de propriedades.
  2. *-screen.hooks.ts: Contém toda a lógica de estado, chamadas de hooks de feature, handlers e efeitos.

Regra de Ouro da Tela

A tela é a única responsável por disparar feedbacks visuais diretos (Toasts, Modais de Alerta, Haptics). Nenhuma outra camada (Feature ou Application) deve fazer isso.


2. Fragmentos de Tela (Fragment Components)

Para evitar que um arquivo de tela cresça demais, dividimos a interface em Fragmentos. Veja o guia detalhado sobre Fragment Components.

Regra de Comunicação dos Fragmentos:

  • Passagem de Propriedades: Um fragmento sempre deve receber seus dados e ações via props.
  • Isolamento de Estado: Um fragmento nunca deve saber como obter informações da API ou acessar o estado global diretamente. Ele é um componente "burro" que apenas renderiza o que a Tela (Pai) envia.

3. Exemplo Completo: Gerenciamento de Usuários

Abaixo, um exemplo de uma tela que utiliza Redux Saga para ações globais e TanStack Query para dados de domínio.

A. O Hook da Tela (users-screen.hooks.ts)

typescript
export function useUsersScreen() {
    // 1. Dados de Domínio (TanStack Query)
    const { data: users, isLoading, refetch } = useGetUsers();
    
    // 2. Estado Local da Tela
    const [searchTerm, setSearchTerm] = useState("");

    // 3. Ação Global (Redux)
    const dispatch = useDispatch();

    const handleLogout = () => {
        // Usamos Redux Saga para processos de infraestrutura/globais (como limpar sessão)
        dispatch(logoutAction());
    };

    const handleUpdateStatus = (id: string, active: boolean) => {
        // As actions são da camada de aplicação e apenas realizam a chamada
        updateUserStatusAction(id, active).then(() => {
            refetch(); // Atualiza a lista via TanStack (Camada Feature)
            Toast.show("Usuário atualizado com sucesso!"); // Feedback visual (Camada Screen)
        }).catch((error) => {
            // O tratamento de erro ocorre aqui na Screen
            Toast.show("Falha ao atualizar usuário");
        });
    };

    return {
        users: users?.filter(u => u.name.includes(searchTerm)) ?? [],
        isLoading,
        searchTerm,
        setSearchTerm,
        handleLogout,
        handleUpdateStatus
    };
}

B. O Componente da Tela (users.screen.tsx)

tsx
import { useUsersScreen } from "./users-screen.hooks";
import { UserListFragment } from "./fragments/user-list-fragment";

function UsersScreen() {
    const { 
        users, 
        isLoading, 
        handleUpdateStatus, 
        handleLogout 
    } = useUsersScreen();

    return (
        <ScreenContainer>
            <Header title="Gestão de Usuários" onLogout={handleLogout} />
            
            {isLoading ? (
                <LoadingState />
            ) : (
                /* Passamos as propriedades para o Fragmento. 
                   O fragmento não sabe de onde vem os 'users' */
                <UserListFragment 
                    data={users} 
                    onAction={handleUpdateStatus} 
                />
            )}
        </ScreenContainer>
    );
}

C. O Fragmento (fragments/user-list-fragment.tsx)

tsx
interface UserListFragmentProps {
    data: User[];
    onAction: (id: string, active: boolean) => void;
}

// O Fragmento recebe TUDO via props.
function UserListFragment({ data, onAction }: UserListFragmentProps) {
    return (
        <List>
            {data.map(user => (
                <UserCard 
                    key={user.id}
                    name={user.name}
                    isActive={user.active}
                    onToggle={() => onAction(user.id, !user.active)}
                />
            ))}
        </List>
    );
}

4. Por que usar cada tecnologia?

Por que Redux Saga?

  • Finalidade: Gerenciar Side-Effects Globais e Infraestrutura.
  • Quando usar: Fluxos de autenticação, sincronização de persistência (Storage), logs de auditoria e estados que precisam persistir durante toda a sessão do app (ex: perfil do usuário logado).
  • Vantagem: Controle total sobre fluxos assíncronos complexos e sequenciais.

Por que TanStack Query?

  • Finalidade: Gerenciar Server State (Dados da API).
  • Quando usar: Listagens, detalhes de registros, caches de busca e qualquer dado que venha do banco de dados e precise de sincronização automática.
  • Vantagem: Gerenciamento automático de cache, estados de loading/error prontos e invalidação inteligente de dados (quando um componente altera um dado, todos os outros que usam a mesma "chave" são atualizados automaticamente).

5. Resumo de Boas Práticas

  1. Componentes sempre como function: Nunca use const para declarar componentes.
  2. Lógica fora da Screen: Se houver um if ou um map complexo que não seja puramente visual, ele deve estar no hook.
  3. Fragmentos Puros: Nunca deixe um fragmento acessar o useSelector ou useQuery. Peça o dado para o pai.
  4. Propagação de Props: Fragmentos são excelentes para dividir a tela, mas lembre-se de tipar as props corretamente para manter a segurança do código.