Hoje iremos entender o que são componentes puros e impuros e como usa-los no React. Como já sabemos no Javascript existem funções puras e impuras, nos componentes do React não é diferente. Seguindo a mesma premissa, ao criar componentes que tenham ao máximo funções puras, pode-se evitar bugs e problemas na manutenção do código a maneira que o projeto cresce.
Caso não lembre ou não saiba o que são funções puras e impuras leia: O que são funções puras e impuras – FrontMaster
O React ele foi criado em cima do conceito de funções puras. A medida que os componentes são criado o React pressupõe que é uma função pura. Então sempre seu componente deve ter o mesmo retorno com as mesmas entradas, lembrando que esse é um dos requisitos para ser uma função pura. Abaixo mostrarei um exemplo retirado da documentação do React.
Componentes puros e impuros
let guest = 0;
function Cup() {
guest = guest + 1;
return <h2>Tea cup for guest #{guest}</h2>;
}
export default function TeaSet() {
return (
<>
<Cup />
<Cup />
<Cup />
</>
);
}

Ao observarmos, cada renderização do componente Cup o valor é diferente, isso é ruim pois quebra um dos requisitos de ser uma função pura, não ter o valor final previsível. Então caso esse componente esteja em outra página o valor de retorno será diferente, pois cada chamada do componente ele retornara um JSX diferente. Vamos ajustar o código para ele depender apenas do parâmetro para ela modificar apenas o próprio escopo e não o escopo global da aplicação. Nessa situação acima o componente modifica uma variável fora do seu escopo.
function Cup({ guest }) {
return <h2>Tea cup for guest #{guest}</h2>;
}
export default function TeaSet() {
return (
<>
<Cup guest={1} />
<Cup guest={2} />
<Cup guest={3} />
</>
);
}

Para o componente ser puro ele não pode alterar variáveis ou objetos que existam antes da renderização. Quando dada as mesmas entradas o componente dever sempre retornar os mesmo resultado. Caso isso não ocorra a função não é pura.
Caso necessário mudar algo e não é possível usando o manipulador de eventos, utilize o useEffect como última opção.
O outro componente não pode ser uma dependência para renderizar o componente.
Não se pode alterar os valores que o componente precisa para renderizar como props, estados e contextos. Devemos tratar esses valores apenas como leitura. Quando necessário alterar algo, crie um estado para isso e não gravar diretamente em uma variável.
Segundo a documentação: Você nunca deve alterar variáveis ou objetos preexistentes enquanto o componente estiver sendo renderizado.
Referência: react.dev