Encontrei uma ocorrência estranha ao trabalhar com Ngrx Store e Angular. Basicamente, o objetivo da minha tarefa é mostrado neste exemplo com cerca de 10 mil entradas em Person[]:
Meu objetivo é mostrar certas pessoas com sobrenomes específicos. Esta é a primeira revisão que fiz, que é bem lenta:
export const selectPerson = createSelector(
selectCitizensState,
(citizensState: CitizensState): Person[] => citizensState.people ?? []
);
export const selectPeopleWithSurname = createSelector(
selectPerson,
selectSelectedSurnames,
(people: Person[], surnames: string[]) => {
if (surnames.length === 0) {
return people;
}
return people.filter((person) => surnames.includes(person.surname));
}
);
Eu criei isso por acidente e é muito mais rápido que o código acima:
export const selectPerson = createSelector(
selectCitizensState,
(citizensState: CitizensState): Person[] => [...(citizensState.people ?? [])]
);
Por que isso acontece? Estou apenas criando uma cópia superficial da propriedade store. Imagino que tenha algo a ver com a detecção de alterações do Ngrx ou algo assim, mas não tenho certeza.
Imutabilidade e memorização são os motivos pelos quais as partes parecem lentas/rápidas.
(citizensState: CitizensState): Person[] => [...(citizensState.people ?? [])]
Na sua versão rápida, está criando uma nova instância de array sempre que o seletor é executado. Os seletores NgRx usam memorização para armazenar em cache e evitar recomputações desnecessárias. Um seletor só é executado novamente quando suas entradas mudam.Na sua versão lenta, você retorna a referência original do repositório. Isso faz com que
selectPeopleWithSurname
você dependa mais da verificação de conteúdo e, possivelmente, execute novamente com mais frequência do que o necessário.Se "people" for a mesma referência de antes, o NgRx assume que ela não mudou. Mas se os sobrenomes mudarem e você ainda retornar a mesma referência de array, o segundo seletor (selectPeopleWithSurname) deverá reavaliar .filter() todas as vezes. Essa
.filter()
chamada para mais de 10 mil registros é custosa. Se você criar um novo array via[...people]
, o NgRx o tratará como uma nova entrada, o que pode evitar reexecuções desnecessárias no fluxo de dados se você estiver usando a detecção de alterações OnPush ou um pipe como*ngFor
.Confira este artigo sobre memorização com seletores no NgRx por Keerti Kotaru