Olá Habr!
Com incrível orgulho e alívio, entregamos um
novo livro sobre React à gráfica hoje à noite.

Nesta ocasião, oferecemos uma tradução ligeiramente abreviada de um artigo de Dan Abramov, descrevendo o uso de interceptores na 16ª versão do React. O livro, pelo qual já estamos ansiosos, é descrito no capítulo 5.
Na semana passada,
Sophie Alpert e eu introduzimos o conceito de "interceptores" na conferência React Conf, seguida por uma
discussão detalhada
sobre o
tópico de
Ryan Florence .
Eu recomendo fortemente que você assista a esta palestra plenária para se familiarizar com a gama de problemas que estamos tentando resolver com a ajuda de interceptadores. No entanto, mesmo a hora do seu tempo que valorizo muito, decidi delinear brevemente neste artigo as principais considerações para interceptadores.
Nota: Os interceptores de reação ainda são experimentais. Não há necessidade de se aprofundar neles agora. Observe também que esta publicação define minhas opiniões pessoais, que podem não coincidir com a posição dos desenvolvedores do React.
Por que os interceptores são necessários?A organização de componentes e o fluxo de dados downstream são conhecidos por ajudar a organizar grandes UIs na forma de fragmentos pequenos, independentes e reutilizáveis.
Entretanto, muitas vezes não é possível dividir componentes complexos além de um certo limite, pois a lógica preserva o estado e não pode ser extraída em uma função ou em outro componente . Às vezes, aqueles que dizem que o React não pode conseguir uma "separação de tarefas" reclamam disso.
Esses casos são muito comuns e estão associados, por exemplo, a animação, processamento de formulários, conexão com fontes de dados externas e muitas outras operações que podemos precisar executar com nossos componentes. Tentando resolver esses problemas apenas com componentes, geralmente obtemos:
- Componentes gigantes difíceis de refatorar e testar.
- Duplicação da lógica entre vários componentes e métodos de ciclo de vida.
- Padrões complexos , em particular, renderizando adereços e componentes de ordem superior.
Acreditamos que os interceptadores são os mais promissores para resolver todos esses problemas.
Os interceptores ajudam a organizar a lógica dentro do componente na forma de unidades isoladas reutilizáveis :

Os interceptores estão alinhados com a filosofia React (fluxo e composição explícitos de dados) e dentro de um componente, não apenas entre componentes . É por isso que me parece que os interceptores se encaixam naturalmente no modelo de componente React.
Ao contrário de padrões como propriedades de renderização ou componentes de ordem superior, os farejadores não sobrecarregam sua árvore de componentes com anexos desnecessariamente profundos. Além disso, eles não têm as
desvantagens inerentes às impurezas.
Mesmo que, à primeira vista, os interceptadores o entortem (como eu no começo!), Recomendo que você dê uma chance a essa opção e faça experiências com ela. Eu acho que você vai gostar.
O React inchaço é devido a interceptores?Até cobrirmos os interceptadores em detalhes, você pode estar preocupado que adicionar interceptores no React seja apenas uma multiplicação de entidades. Isso é uma crítica justa. Penso o seguinte: embora, a curto prazo, você realmente sinta uma carga cognitiva extra (para estudá-los), no final, apenas se sentirá melhor.
Se os interceptores se enraizarem na comunidade React, na verdade o número de entidades que precisam ser gerenciadas ao escrever aplicativos React será reduzido . Usando interceptores, você pode constantemente usar funções, em vez de alternar entre funções, classes, componentes de ordem superior e renderização de componentes.
Quanto ao aumento no tamanho da implementação, o aplicativo React, com o suporte de interceptores, aumenta apenas cerca de ~ 1,5kB (min + gzip). Embora isso por si só não seja muito, é muito provável que,
ao usar interceptores, o tamanho do seu conjunto diminua , pois o código do interceptador geralmente é minificado melhor do que o código equivalente usando classes. O exemplo a seguir é um pouco extremo, mas demonstra claramente por que tudo é assim (
clique para expandir o segmento inteiro):
Não há mudanças revolucionárias na proposta do interceptador . Seu código funcionará bem, mesmo se você começar a usar interceptores em novos componentes. De fato, é exatamente isso que recomendamos: não reescreva nada globalmente! Seria sensato esperar até que o uso de interceptores seja estabelecido em todo o código crítico. No entanto, ficaremos gratos se você puder experimentar a versão alpha 16.7 e nos deixar um feedback sobre a
proposta de interceptadores , além de
relatar quaisquer erros .
O que é isso - interceptadores?Para entender o que são interceptores, você precisa voltar um passo e pensar sobre o que é a reutilização de código.
Atualmente, existem muitas maneiras de reutilizar a lógica nos aplicativos React. Portanto, para calcular algo, você pode escrever funções simples e depois chamá-las. Você também pode escrever componentes (que podem ser funções ou classes). Os componentes são mais poderosos, mas ao trabalhar com eles, é necessário exibir alguma interface do usuário. Portanto, o uso de componentes é inconveniente para transmitir lógica não visual. Então, chegamos a padrões complexos, como propriedades de renderização e componentes de ordem superior.
O React não tornaria mais fácil se houvesse apenas uma maneira geral de reutilizar o código, e não tanto?As funções parecem perfeitas para código reutilizável. Passar a lógica entre funções é o menos caro. No entanto, o estado local do React não pode ser armazenado nas funções. Você não pode extrair comportamentos como "controlar o tamanho da janela e atualizar o estado" ou "animar um valor por algum tempo" de um componente de classe sem reestruturar o código ou sem introduzir abstrações como Observables. Ambas as abordagens apenas complicam o código, e o React é agradável para nós com sua simplicidade.
Os interceptores resolvem esse mesmo problema. Graças aos interceptores, você pode usar os recursos React (por exemplo, estado) de uma função - chamando-a apenas uma vez. O React fornece vários interceptores internos que correspondem aos tijolos do React: estado, ciclo de vida e contexto.
Como os interceptores são funções regulares do JavaScript, você pode combinar os interceptores internos fornecidos no React para criar "interceptores nativos" . Assim, problemas complexos podem ser resolvidos com uma única linha de código e, em seguida, multiplicá-lo em seu aplicativo ou
compartilhá-lo na comunidade ReactCuidado: estritamente falando, seus próprios interceptores não estão entre os recursos do React. A capacidade de escrever seus próprios interceptores decorre naturalmente de sua organização interna.
Mostre-me o código!Suponha que desejemos inscrever um componente na largura atual da janela (por exemplo, para exibir outro conteúdo ou uma área de visualização mais estreita).
Código semelhante pode ser escrito hoje de várias maneiras. Por exemplo, para criar uma classe, crie vários métodos de ciclo de vida, ou talvez até recorra a propriedades de renderização ou aplique um componente de ordem superior se você estiver procurando por reutilização. No entanto, acho que nada se compara a isso:
gist.github.com/gaearon/cb5add26336003ed8c0004c4ba820eaeSe você ler este código, significa que ele faz exatamente o que diz . Usamos a largura da janela dentro do nosso componente, e o React redesenha o seu componente se ele mudar. É exatamente para isso que os interceptores são necessários - para tornar os componentes realmente declarativos, mesmo que contenham efeitos colaterais e de estado.
Considere como esse próprio interceptador pode ser implementado. Poderíamos usar o estado React local para manter a largura atual da janela e definir o estado quando a janela é redimensionada usando um efeito colateral:
gist.github.com/gaearon/cb5add26336003ed8c0004c4ba820eaeComo mostrado acima, os interceptores
useState
do React, como
useState
e
useEffect
servem como tijolos. Podemos usá-los diretamente de nossos componentes ou montar nossos próprios interceptores a partir deles, por exemplo,
useWindowWidth
. Usar seus próprios interceptores não parece menos idiomático do que trabalhar com a API do React integrada.
Leia mais sobre os interceptores internos
nesta revisão .
Os interceptores são encapsulados - cada vez que o interceptador é chamado, ele recebe um estado local isolado dentro do componente atualmente em execução . Neste exemplo em particular, isso não é importante (a largura da janela é a mesma para todos os componentes!), Mas é exatamente isso que é o poder dos interceptadores! Eles pretendem separar não a lógica de estado, mas a preservação de estado.
Não queremos interromper o fluxo de dados a jusante!Cada interceptador pode conter algum estado local e efeitos colaterais. Você pode transferir dados entre vários interceptores, como é geralmente feito entre funções. Eles podem aceitar argumentos e retornar valores porque são funções JavaScript.
Aqui está um exemplo de uma biblioteca de animação React, onde experimentamos interceptores:
Observe como a animação impressionante é implementada no código-fonte mostrado: passamos valores entre vários interceptores nativos na mesma função de renderização.
codesandbox.io/s/ppxnl191zx(Este exemplo é discutido em mais detalhes
neste guia .)
Devido à capacidade de transferir dados entre interceptores, eles são muito convenientes para implementar animações, assinar dados, gerenciar formulários e trabalhar com outras abstrações com estado.
Diferentemente das propriedades de renderização ou dos componentes de ordem superior, os interceptores não criam uma "falsa hierarquia" na sua árvore de renderização . Eles são mais como uma lista bidimensional de "células de memória" anexadas a um componente. Sem níveis extras.
E as aulas?Em nossa opinião, nossos próprios interceptores são os detalhes mais interessantes de toda a oferta. Mas, para que seus próprios interceptadores funcionem, o React deve fornecer, no nível das funções, a capacidade de declarar o estado e os efeitos colaterais. É exatamente isso que nos permite fazer interceptadores
useState
como
useState
e
useEffect
. Leia mais sobre isso na
documentação .
Acontece que esses interceptores embutidos são convenientes não apenas ao criar seus próprios interceptores. Eles também são suficientes para determinar os componentes como um todo, porque nos fornecem os recursos necessários - por exemplo, estado. É por isso que gostaríamos que os interceptores se tornassem o principal meio para definir componentes do React no futuro.
Não, não planejamos abolir gradualmente as aulas. Usamos dezenas de milhares de componentes de classe no Facebook e nós (como você) absolutamente não queremos reescrevê-los. Porém, se a comunidade React começar a usar interceptores, será inadequado preservar as duas maneiras recomendadas de escrever componentes. Os interceptores abrangem todos os casos práticos nos quais as classes são usadas, mas oferecem maior flexibilidade ao extrair, testar e reutilizar o código. É por isso que conectamos interceptores com nossas idéias sobre o futuro do React.
E se os interceptores forem mágicos?Talvez
as regras do interceptador o confundam.
Embora não seja habitual chamar um interceptador no nível superior, você provavelmente não desejaria determinar a condição na condição, mesmo que pudesse . Por exemplo, um estado vinculado à condição não pode ser determinado na sala de aula e, durante quatro anos de comunicação com os usuários do React, não ouvi nenhuma reclamação sobre isso.
Esse projeto é fundamental para a introdução de seus próprios interceptores sem introduzir ruído de sintaxe excessivo ou criar armadilhas. Entendemos que por hábito é difícil, mas acreditamos que esse compromisso é aceitável, dadas as oportunidades que ele oferece. Se você não concorda, sugiro que experimente você mesmo e tente como você gosta dessa abordagem.
Estamos usando ganchos de produção há um mês para ver se as novas regras confundem os programadores. A prática mostra que uma pessoa domina com interceptores em questão de horas. Confesso que, para mim, essas regras à primeira vista pareciam heresia, mas esse sentimento passou rapidamente. Essa foi a impressão que tive quando conheci o React. (Você não gostou do React? E eu só gostei pela segunda vez.)
Atenção: no nível de implementação dos interceptores também não há mágica. De acordo
com Jamie , ela recebe algo assim:
gist.github.com/gaearon/62866046e396f4de9b4827eae861ff19Mantemos uma lista explodida de interceptores e passamos para o próximo componente da lista cada vez que você usa um interceptador. Graças às regras dos interceptadores, sua ordem é a mesma em qualquer mecanismo de renderização, portanto, a cada chamada, podemos fornecer ao componente o estado correto.
(
Neste artigo de Rudy Yardley, tudo está maravilhosamente explicado nas fotos!)
Talvez você tenha se perguntado onde o React armazena o estado dos interceptadores. No mesmo local que o estado das classes. O React possui uma fila de atualização interna que contém a verdade última para cada estado, independentemente de como você define seus componentes.
Os interceptores são independentes de proxies e getters, que são tão comuns nas modernas bibliotecas JavaScript. Portanto, pode-se argumentar que há menos magia nos interceptores do que em outras abordagens populares para resolver esses problemas. Não mais do que em
array.push
e
array.pop
(no caso em que a ordem das chamadas também é importante!)
O design do interceptor não está vinculado ao React. De fato, alguns dias após a publicação da proposta, várias pessoas nos mostraram implementações experimentais da mesma API interceptora para o Vue, componentes da Web e até funções JavaScript comuns.
Por fim, se você é fanaticamente dedicado à programação funcional e não se sente à vontade quando o React começa a confiar em um estado mutável como parte de uma implementação. Porém, pode ser reconfortante que o processamento interceptador possa ser implementado em sua forma pura, limitando-se a efeitos algébricos (se eles forem suportados em JavaScript). Naturalmente, no nível intra-sistema, o React sempre conta com um estado mutável - e é exatamente isso que você gostaria de evitar.
Independentemente de qual ponto de vista está mais próximo de você - pragmático ou dogmático -, espero que pelo menos uma dessas opções lhe pareça lógica. Mais importante, na minha opinião, os interceptadores simplificam nosso trabalho, e fica mais conveniente para os usuários trabalharem. É isso que os interceptadores me subornam assim.