Reescrevendo a biblioteca 반충 파라 React em 100 linhas
33863 단어 reactptbrwebdevjavascript
Recoiléuma nova biblioteca do React,escrita por algumas pessoas do Facebook que trabalham em uma ferramenta chamada Ela surgiu devido a problemas de ergonomia edesempenho com
context
euseState
._mabiblioteca muito inteligente e quase todo mundo encontraáuma utilidade para ela-confira este se quiser saber mais.come ço eu fiquei realmente suprreso com a conversa sobre teoria dos grá ficos e a má gica maravillhosa que o Recoil executa,mas deposis de um tempo comecei a ver que talvez n ão seja t ão special assim이 없습니다.Aqui estáminha chance de implementar algo semelhante!
Antes de começar,observe que a maneira como implementei meu clone do Recoilécompletamente diferente de como o Recoil realéimplementado.Não presuma nada sobre backil disso.
Átomos
O backiléconstruído em torno do conceito de“átomos”.Osátomos são pequenas partes atômicas de estado que vocêpode assinar e atualizar em seus components.
Para começar,vou criar uma classe chamada
Atom
que vai envolver algum valorT
.유럽연합adicionei métodos Auxiliarsupdate
esnapshot
parapermitirque vocêobtenhae defina o valor.class Atom<T> {
constructor(private value: T) {}
update(value: T) {
this.value = value;
}
snapshot(): T {
return this.value;
}
}
Para ouvir as mudanças no estado,vocêprecisa usaro padrão do observador.Issoécomumente visto em bibliotecas comoRxJS,mas,neste caso,vou escrev uma versão síncrona simples do zero.Parasaberquemestáouvindooestado,usoum
Set
com 리셋.UmSet
(ou concento de hash)uma estrutura de dados que contém apenas itensúnicos.Em JavaScript,ele pode ser facilmente transformado Em um array e possui métodosúteis para adicionar e remover itens rapidamente.A adição de um ouvinteéfeita por meio do método
subscribe
.OmétodosubscribereturnaDisconnecter
-uma 인터페이스 내용umétodoqueimpediráumouvintedeescutar.Issoéchamado quando um componente Reactédesmontado e vocènão deseja mais ouvir as alteraçes.Em seguida,um método chamado
emit
éadicionado.Este método percorre cada um dos ouvintes e fornece a eles o valor atual do estado.Por fim,atualizo o método
update
para emitir os novos valores sempre que o estado for definido.type Disconnecter = { disconnect: () => void };
class Atom<T> {
private listeners = new Set<(value: T) => void>();
constructor(private value: T) {}
update(value: T) {
this.value = value;
this.emit();
}
snapshot(): T {
return this.value;
}
emit() {
for (const listener of this.listeners) {
listener(this.snapshot());
}
}
subscribe(callback: (value: T) => void): Disconnecter {
this.listeners.add(callback);
return {
disconnect: () => {
this.listeners.delete(callback);
},
};
}
}
오파!Éhora de escrever oá tomo em nossos 구성 요소 반응.Para fazer isso,Crei um gancho chamado
useCoiledValue
.( soa familiar? )Este gancho returna o estado atual de umátomo,e escuta e renderiza novatement e sempre que o valor mudar.Sempre que o ganchoédesmontado,ele desconecta o ouvinte.
Uma coisa um pouco estranha aquiéo gancho
updateState
.Ao executar um estado definido com uma nova referência de objeto({}
),o React irárenderizar novamente o component.Issoéum pouco um hack,maséuma maneira fácil de garantir que o componente seja renderizado Novatemente.export function useCoiledValue<T>(value: Atom<T>): T {
const [, updateState] = useState({});
useEffect(() => {
const { disconnect } = value.subscribe(() => updateState({}));
return () => disconnect();
}, [value]);
return value.snapshot();
}
Em seguida,adicionei um métodouseCoiledState
.Tem uma API muito semelhante auseState
-fornece o valor atual do estado e permite que vocêdefina um novo.export function useCoiledState<T>(atom: Atom<T>): [T, (value: T) => void] {
const value = useCoiledValue(atom);
return [value, useCallback((value) => atom.update(value), [atom])];
}
Agora que implementamos esses ganchos,éhora de passar para os seletores.Antes disso,vamos refatorar um pouco que temos.Um seletoréUm valor com estado,assim como Umátomo.Para tornar a implementaão deles um pouco mais fácil,moverei a maior parte da lógica de
Atom
Para uma classe base chamadaStateful
.class Stateful<T> {
private listeners = new Set<(value: T) => void>();
constructor(private value: T) {}
protected _update(value: T) {
this.value = value;
this.emit();
}
snapshot(): T {
return this.value;
}
subscribe(callback: (value: T) => void): Disconnecter {
this.listeners.add(callback);
return {
disconnect: () => {
this.listeners.delete(callback);
},
};
}
}
class Atom<T> extends Stateful<T> {
update(value: T) {
super._update(value);
}
}
Seguindo em frente!Seletores 회사
Um seletoréa versão de backil de“valores computados”ou“redutores”.Em suaspróprias palavras:
Um seletor representa uma parte derivado do estado. Você pode pensar no estado derivado como a saída da passagem do estado para uma função pura que modifica o estado fornecido de alguma forma.
API para seletores no backilébastante simples,vocêcria um objeto com um método chamado
get
e tudo que esse método returnaéo valor do seu estado.Dentro do métodoget
,vocêpode assinar outras partes do estado e,sempre que elas forem atualizadas,o seu seletor também será.Em nosso caso,vou renomear o método
get
a ser chamado degenerator
.Estou chamando-o assim porqueéessencialmente uma funão de fábrica que deve gerar o próximo valor do estado,com base em tudo que canalizado para ele.código가 없으면podemoscapturaressemétodo
generate
는tipo의 조수입니다.type SelectorGenerator<T> = (context: GeneratorContext) => T;
Para aqueles que não estão familizados com TypeScript,essaéuma funão que recebe um objeto de contexto(GeneratorContext
)como par–metro e returna algum valorT
.Esse valor de returnoéo que se torna o estado interno do seletor.O que O objeto
GeneratorContext
faz?Bem,éassim que os seletores usam outras partes do estado ao gerar seu próprio estado interno.De agora em diante,essas partes do estado como'dependências'를 인용할 수 있도록 허락해 주십시오.
interface GeneratorContext {
get: <V>(dependency: Stateful<V>) => V
}
Sempre que alguém chama o métodoget
noGeneratorContext
,ele adiciona um pedaço de estado como uma dependencia.atualizada, Isso signific que sempre que uma dependência, o seletor também será.우리는 선거에서 승리할 것이다.
function generate(context) {
// Registra "NameAtom" como dependência
// e retorna seu valor
const name = context.get(NameAtom);
// Faz o mesmo para "AgeAtom"
const age = context.get(AgeAtom);
// Retorna um novo valor usando os átomos anteriores
// Ex: "Bob is 20 years old"
return `${name} is ${age} years old.`;
};
Com a funão de geraão de estado fora do caminho,vamos criar a classeSelector
.Essa classe deve aceitar a fun227;o de geraão como um par–metro do construtor e usar um métodogetDep
na classe para returnar o valor dasAtom
de dedededependencias.Vocêpode notar no construtor que escrevi
super(undefined as any)
.Isso ocorre porquesuper
deve ser a primeira linha no construtor de uma classe derivida.Se ajudar,neste caso vocêpode pensar emundefined
como uma memória não inicializada.export class Selector<T> extends Stateful<T> {
private getDep<V>(dep: Stateful<V>): V {
return dep.snapshot();
}
constructor(
private readonly generate: SelectorGenerator<T>
) {
super(undefined as any);
const context = {
get: dep => this.getDep(dep)
};
this.value = generate(context);
}
}
Este seletor sóbom para gerar estado uma vez.Para reagirás mudanças nas Dependencias,precisamos assinálas.Para fazer isso,vamos atualizar o método
getDep
Para assinar as Dependencias e chamar o métodoupdateSelector
.Para garantir que o seletor seja atualizado apenas uma vez a cada alteraão,vamos acompanhar as depenências usando umSet
.O método
updateSelector
émuito semelhante ao construtor do exemplo front.Ele criaGeneratorContext
,executa o métodogenerate
e então usa o métodoupdate
da classe baseStateful
.export class Selector<T> extends Stateful<T> {
private registeredDeps = new Set<Stateful>();
private getDep<V>(dep: Stateful<V>): V {
if (!this.registeredDeps.has(dep)) {
dep.subscribe(() => this.updateSelector());
this.registeredDeps.add(dep);
}
return dep.snapshot();
}
private updateSelector() {
const context = {
get: dep => this.getDep(dep)
};
this.update(this.generate(context));
}
constructor(
private readonly generate: SelectorGenerator<T>
) {
super(undefined as any);
const context = {
get: dep => this.getDep(dep)
};
this.value = generate(context);
}
}
당장 취소!반충tem algumas fun ões AUXILIARS para criar á tomos e seletores.Como a maioria dos desenvolvedores de JavaScript는 이를 Como má prática,eles ajudar ão a mascarar nossas cratades 클래스로 간주합니다.어, 여덟 번째, 우마토모...
export function atom<V>(
value: { key: string; default: V }
): Atom<V> {
return new Atom(value.default);
}
E um para criar um seletor...export function selector<V>(value: {
key: string;
get: SelectorGenerator<V>;
}): Selector<V> {
return new Selector(value.get);
}
오, lembra daquele ganchouseCoiledValue
de antes?Vamos atualizar isso para aceitar seletores também:export function useCoiledValue<T>(value: Stateful<T>): T {
const [, updateState] = useState({});
useEffect(() => {
const { disconnect } = value.subscribe(() => updateState({}));
return () => disconnect();
}, [value]);
return value.snapshot();
}
Éisso aí!콘세쿠모스!🎉Dèum tapinha nas suas costas!
아카바도?
Por uma questão de brevidade(e para usar aquele título de“100 linhas”para ganhar uns claiques),decidi omitir comentários,testes e exemplos.Se vocèquiser uma explicaèo mais completa(ou quiser brincar com examplos),tudo isso estáno meurepositório “recoil-clone” do Github.
Também háumexemplo de siteao vivo para que vocêpossa testálo.
결론
Umavez lique todo bom 소프트웨어 개발자는 o suficiente para qualquer pessoa possa reescrevêlo, se necess ário를 간소화했다.O 반충tem muitos recursos que n ã O implementei aqui,mas é empolgante verum design t ã O simples e inquitivo que pode ser razoavelmente implementado manualmente.
Antes de decidir lançar meu Bootlog Backil em produço,certifique se de verificar o seguinte:
useMutableSource
.Se vocêestiver usando uma versão recente do React,vocêdeve usá-lo ao invés desetState
emuseCoiledValue
.key
para cadaátomo e seletor queéusado como metadados para um recurso chamado“observaãO em todo O aplicativo”.Eu o inclu í apesar de n ão us á-lo para manter a API 익숙합니다.Deposis de escrever este post,vi a bibliotecajotai._mconcento de recursos muito semelhante ao meu 클론 e of erece suporte ass íncrono!
Créditos 회사
Rewriting Facebook's "Recoil" React library from scratch in 100 lines,escrito originalmente por.
Reference
이 문제에 관하여(Reescrevendo a biblioteca 반충 파라 React em 100 linhas), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/oieduardorabelo/reescrevendo-a-biblioteca-recoil-para-react-em-100-linhas-k76텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)