Ir para o conteúdo
Gerenciamento de estado Angular: práticas recomendadas para avançar em seu projeto

Gerenciamento de estado Angular: práticas recomendadas para avançar em seu projeto

Quais são algumas das melhores estratégias e práticas para uma gestão eficaz do Estado Angular? Leia aqui e descubra como melhorar o desempenho do seu aplicativo.

14min read

Quando estamos criando aplicativos Angular com comunicações de dados pesadas, precisamos considerar uma abordagem holística, abordando fatores como eficiência de dados, latência de rede, escalabilidade, gerenciamento de recursos, testes e UX. E uma das coisas que é extremamente vital para evitar conflitos de dados, mantendo o aplicativo escalável e consistente, é o Angular State Management eficaz. Sem ele, os dados estarão em toda parte.

Mas o que é Gestão de Estado em Angular e como exatamente ela deve ser tratada? Vamos nos aprofundar Angular práticas recomendadas de gerenciamento de estado com Ignite UI for Angular e ver como abordá-lo com uma implementação básica de serviço utilizandoRxjse gerenciamento de estado comNgRx, com suporte de exemplos de código rreal.

Try Ignite UI Angular For Free

O que é gerenciamento de estado em Angular

Este é um processo que envolve diferentes técnicas e estratégias para lidar e manter o estado de Angular aplicações. Nesse caso, o estado de um aplicativo Web refere-se a todos os dados usados pelo aplicativo, incluindo dados buscados de serviços externos e manipulados e exibidos pelo aplicativo, e também todos os dados que o aplicativo cria, armazena e usa para criar a interface do usuário correta para um determinado usuário.

Em palavras simples, você pode se perguntar por que precisamos de uma gestão estatal em Angular. Pense nisso – cada nova página ou nova funcionalidade, cada nova interface do usuário no aplicativo que indica uma condição atual (por exemplo, se um usuário está conectado ou não) aumenta a complexidade de um aplicativo. A maneira mais comum de fazer isso em Angular é um contêiner de estado, que não apenas recupera, compartilha e atualiza os dados, mas também permite que eles sejam acessados, interagidos e modificados de maneira limpa e bem organizada.

Fazendo a ligação ou sabendo quando é necessário

À medidaque o aplicativo é dimensionado e se torna mais complexo, isso geralmente significa lidar com muitos dados, e gerenciar dados em grandes projetos pode ser complicado. Aqui, as coisas ficam ainda mais confusas quando você trabalha com dados assíncronos, mais alterações de dados a serem rastreadas e mais código. É provável que você acabe com muitas coisas acontecendo nos bastidores, como computação de mudança constante, refluxos ou solicitações de API, o que pode até bloquear o thread principal do navegador e fazer com que o aplicativo pare de responder. Se não for abordado sistematicamente com a gestão estadual, o acima significaria muitas despesas para corrigir bugs, dívidas técnicas, etc.

Então, quando usar o State Management Angular? Aqui estão alguns casos de uso.

  • Crescente complexidade do aplicativo

Embora aplicativos simples com fluxo mínimo de dados possam não exigir gerenciamento de estado, ele é especialmente benéfico em projetos de software de grande escala com vários componentes, muitas interações de dados, um conjunto crescente de recursos e interfaces de usuário complexas.

O que é alcançado aqui: manter a ordem, a consistência dos dados, melhor desempenho e UX + aprimorado fornecem interfaces responsivas.

  • Sharing data 

O gerenciamento de estado torna-se essencial quando os dados precisam ser compartilhados entre diferentes partes do seu aplicativo, como componentes ou serviços.

O que é alcançado aqui: gerenciamento centralizado de dados e compartilhamento simplificado de dados.

  • Em operações assíncronas

Se o app envolver operações assíncronas, como buscar dados de APIs, lidar com atualizações em tempo real ou lidar com interações do usuário.

O que é alcançado aqui: fluxo de dados assíncrono simplificado e interface do usuário responsiva.

  • Ao visar mudanças de estado previsíveis

Você deseja um controle rigoroso sobre como e quando o estado muda em seus aplicativos Angular? Existem diferentes bibliotecas de gerenciamento de estado que podem facilitar o processo.

O que é alcançado aqui: base de código previsível e fácil de depurar.

Facilitando o trabalho dos desenvolvedores: Angular as melhores práticas de gerenciamento de estado com Ignite UI

Quanto a como gerenciar o estado no Angular, existem várias técnicas e ferramentas, dependendo da complexidade do aplicativo, requisitos do projeto, além de seus conhecimentos e preferências. Por exemplo, se o estado for específico de um único componente, você poderá gerenciar o estado em Angular dentro do próprio componente. Para armazenar e atualizar dados aqui, você terá que confiar nas propriedades e variáveis da classe. Quando se trata de aplicativos de grande escala, é melhor considerar o uso de bibliotecas de gerenciamento de estado.

Mas vamos examinar isso em detalhes e explorar as melhores técnicas de gerenciamento de estado Angular.

  • Implementação básica de serviço utilizando Rxjs como uma ótima maneira de gerenciar o estado

O gerenciamento de estado em Angular usando Serviços e RxJS é uma abordagem poderosa e flexível e aqui estão algumas práticas recomendadas:

  • Single responsibility principle 

Certifique-se de que cada serviço tenha uma única responsabilidade. Ao projetar serviços para lidar com tarefas específicas (por exemplo, registrar e autenticar um usuário, gerenciar dados do usuário, etc.), você mantém sua base de código limpa e sustentável. Cada serviço deve gerenciar uma parte específica do estado, facilitando a depuração e o teste.

  • Serviço como uma única fonte de verdade

Trate o serviço como uma única fonte de verdade para o estado. Os componentes devem contar com o serviço para obter informações e atualizações de estado, promovendo um fluxo de dados unidirecional e facilitando o raciocínio sobre o aplicativo.

  • Encapsular lógica de estado em serviços

Mantenha a lógica de estado dentro dos serviços, não nos componentes. Os serviços devem lidar com a lógica de negócios e o gerenciamento de estado, enquanto os componentes se concentram na apresentação e na interação. Essa separação de preocupações aumenta a capacidade de teste e reutilização.

  • Error handling 

Implemente o tratamento de erros em seus serviços. Capture e trate erros em seus observáveis para fornecer uma experiência de usuário tranquila e evitar que o aplicativo falhe.

  • Utilizando operadores e assuntos RxJs para gerenciamento de estado

Para fazer isso, primeiro escolha o assunto certo – um objeto multicast básico, emitindo valores para vários assinantes sem manter nenhum estado. Adequado para eventos ou ações. Em seguida, defina Assunto do comportamento. É um tipo de Assunto que requer um valor inicial e emite seu valor atual para novos assinantes. Isso é útil para o gerenciamento de estado, pois permite inicializar o estado e sempre fornecer o estado mais recente para novos assinantes. ReplaySubject: emite um número especificado de valores anteriores para novos assinantes. Útil quando você precisa reproduzir valores de estado anteriores. Em seguida, com AsyncSubject, você pode emitir o último valor (e somente o último valor) na conclusão. É útil para cenários em que apenas o valor final emitido é necessário.

Como etapa 2, você pode usar vários fluxos de estado. Use operadores RxJS como combineLatest,withLatestFrom, etc., para combinar vários fluxos de estado. Muitas vezes, você precisa derivar um estado com base em vários observáveis. Operadores como combineLatest permitem que você crie novos observáveis de estado com base na combinação de outros fluxos de estado. Em seguida, escreva testes de unidade para serviços e sua lógica de gerenciamento de estado. Garantir que seus serviços funcionem conforme o esperado por meio de testes é crucial para manter a estabilidade do aplicativo e detectar problemas antecipadamente.

Ao usar um serviço, você centraliza a lógica para buscar e armazenar dados meteorológicos. Isso garante que todos os componentes que precisam de dados meteorológicos os obtenham de uma única fonte de verdade.

Serviço meteorológico

interface WeatherData {
    temperature: number;
   description: string;
}
@Injectable({
  providedIn: 'root'
})
export class WeatherService {
  private apiKey = 'YOUR_API_KEY';
  private _weather: BehaviorSubject<WeatherData | null> = new BehaviorSubject<WeatherData | null>(null);
  public weather$: Observable<WeatherData | null> = this._weather.asObservable();
constructor(private http: HttpClient) { }
  private getApiUrl(location: string): string {
   return `https://api.weatherapi.com/v1/current.json?key=${this.apiKey}&q=${location}`;
  }
  fetchWeather(location: string): Observable<WeatherData | null> {
    const apiUrl = this.getApiUrl(location);
    return this.http.get<WeatherData>(apiUrl).pipe(
      tap((data: WeatherData) => this._weather.next(data)),
      catchError(error => {
        console.error('Error fetching weather data', error);
        this._weather.next(null);
        return of(null);
      })
    );
  }
}

O'WeatherService'busca dados da API e os armazena em um'BehaviorSubject', tornando-o acessível a qualquer componente que injete o serviço. Usando'BehaviorSubject'do​ ​rxjs. permite que seu aplicativo gerencie reativamente as alterações de dados. Quando os dados meteorológicos são atualizados no serviço, todos os componentes inscritos no'BehaviorSubject'são notificados automaticamente e podem atualizar suas visualizações de acordo.

Component TS file: 

export class WeatherDisplayComponent implements OnInit {
    weather$: Observable<WeatherData | null>;

    constructor(private weatherService: WeatherService) {
        this.weather$ =this.weatherService.weather$;
    }

    ngOnInit(): void {
        this.weatherService.fetchWeather('New York');
    }
}

Arquivo HTML do componente:

<div *ngIf="weather$ | async as weather"> 
   <h2>Current Weather</h2> 
   <p>Temperature: {{ weather.temperature }}°C</p> 
   <p>Description: {{ weather.description }}</p> 
</div> 

Os componentes permanecem focados na exibição de dados e no tratamento de interações do usuário sem se preocupar com a busca de dados ou o gerenciamento de estado. Essa separação de preocupações torna os componentes mais fáceis de escrever, testar e manter.

Aqui está um exemplo de gerenciamento de estado com NgRx:

Usar o NgRx para gerenciamento de estado em seu aplicativo Angular pode fornecer benefícios adicionais, especialmente para aplicativos maiores e mais complexos. NgRx é uma biblioteca para gerenciar o estado reativo em aplicativos Angular usando o padrão Redux.

Defina a estrutura de seus dados meteorológicos:

weather.model.ts 

export interface WeatherData {
       temperature: number;
       description: string;
    }
export interface WeatherState {
       weather: WeatherData | null; 
       loading: boolean; 
       error: string | null; 
    }

Defina Ações para representar diferentes eventos no recurso de clima:

weather.actions.ts 

export const loadWeather = createAction(
      '[Weather] Load Weather',
      props<{ location: string }>()
    );
    export const loadWeatherSuccess = createAction(
     '[Weather] Load Weather Success',
      props<{ weather: WeatherData }>()
    );
    export const loadWeatherFailure = createAction(
      '[Weather] Load Weather Failure', 
      props<{ error: string }>()
    );  

Defina o redutor para lidar com alterações de estado com base em ações

weather.reducer.ts

export const initialState: WeatherState = {
      weather: null,
      loading: false,
      error: null,
    };
    export const weatherReducer = createReducer(
      initialState,
      on(loadWeather, (state) => ({
       ...state,
       loading: true,
       error: null,
      })),
      on(loadWeatherSuccess, (state, { weather }) => ({
       ...state,
       weather,
       loading: false,
      })),
     on(loadWeatherFailure, (state, { error }) => ({
       ...state,
       loading: false,
       error,
      }))
    );

Lidar com efeitos colaterais para lidar com efeitos colaterais, como chamadas de API:

weather.effects.ts 

@Injectable()
export class WeatherEffects {
  loadWeather$ = createEffect(() =>
    this.actions$.pipe(
      ofType(loadWeather),
      mergeMap(action => this.http.get<WeatherData>(`https://api.weatherapi.com/v1/current.json?key=YOUR_API_KEY&q=${action.location}`).pipe(
        map(weather => loadWeatherSuccess({ weather })),
        catchError(error => of(loadWeatherFailure({ error: error.message })))
      )
      ))
  );
  constructor(private actions$: Actions, private http: HttpClient) { }
}

Em seu app.module.ts, registre o repositório e os efeitos do NgRx para que eles possam ser usados em seu aplicativo.

@NgModule({
       imports: [
        BrowserModule,
        HttpClientModule,
        StoreModule.forRoot({ weather: weatherReducer }),
        EffectsModule.forRoot([WeatherEffects]),
       ]
    }) 

Defina seletores para o estado do clima para derivar partes do estado do recurso do clima.

weather.selectors.ts 

export const selectWeatherState = createFeatureSelector<WeatherState>('weather');
export const selectWeather = createSelector(
  selectWeatherState,
  (state: WeatherState) => state.weather
);
export const selectLoading = createSelector(
  selectWeatherState,
  (state: WeatherState) => state.loading
);
export const selectError = createSelector(
  selectWeatherState,
  (state: WeatherState) => state.error
);

Injete o armazenamento em seu componente e despache ações para carregar dados meteorológicos e selecione o estado a ser exibido.

weather-display.component.ts 

export class WeatherDisplayComponent implements OnInit {
      weather$: Observable<WeatherData | null>;
      loading$: Observable<boolean>;
      error$: Observable<string | null>;
      constructor(private store: Store<{ weather: WeatherState }>) { 
       this.weather$ = this.store.pipe(select(selectWeather)); 
       this.loading$ = this.store.pipe(select(selectLoading)); 
       this.error$ = this.store.pipe(select(selectError)); 
      }
      ngOnInit(): void { 
       this.store.dispatch(loadWeather({ location: 'New York' })); 
      }
    }

Arquivo HTML do componente:

<div *ngIf="loading$ | async">Loading...</div>
<div *ngIf="error$ | async as error">{{ error }}</div>
<div *ngIf="weather$ | async as weather">
   <h2>Current Weather</h2>
   <p>Temperature: {{ weather.temperature }}°C</p>
   <p>Description: {{ weather.description }}</p>
</div>

2. Considere o uso de bibliotecas de gerenciamento de estado – NgRx 

NgRx é uma biblioteca poderosa para gerenciar o estado em aplicativos Angular usando princípios de programação reativa. Ele é inspirado no padrão Redux e se integra bem ao Angular, facilitando a manutenção e a depuração do estado do seu aplicativo.

Gerenciamento de estado Angular com NgRx

Só para deixar as coisas mais claras, aqui estão os principais conceitos explicados:

Armazenar– Garante o gerenciamento de estado consistente, promove o fluxo de dados unidirecional e fornece uma maneira estruturada de lidar com o estado do aplicativo. Ele representa todo o estado do aplicativo como uma árvore de objetos imutável. Cada nó nessa árvore corresponde a uma parte específica do estado do aplicativo, acessada por meio de observáveis, permitindo que os componentes assinem fatias específicas do estado.

Ações– Eles descrevem eventos em aplicativos baseados em NgRx. Eles representam algo que ocorreu dentro do aplicativo e podem ser considerados uma declaração de fato.

Redutores– Funções que usam o estado atual e uma ação e retornam um novo estado. Os redutores especificam como o estado muda em resposta às ações.

Seletores: funções usadas para selecionar fatias do estado do armazenamento. Eles ajudam a encapsular a estrutura de estado e podem ser compostos para criar seletores mais complexos.

Efeitos– Os efeitos colaterais são tratados fora da loja. Efeitos são classes que escutam ações específicas e executam tarefas como chamadas de API e despacham novas ações com base nos resultados. Eles usam Angular Injeção de Dependência e aproveitam os observáveis RxJS para gerenciar os efeitos colaterais.

Dicas e truques rápidos ao usar o NgRx

Em primeiro lugar, organize seu estado e execute a separação de estado de recursos. Para fazer isso, separe o estado por módulos de recursos. Cada módulo de recurso deve ter sua própria fatia do estado para manter as coisas modulares e sustentáveis. Use uma estrutura de pastas consistente em cada módulo de recurso, como ações, redutores, seletores e efeitos.

Em seguida, você pode definir tipos de ação claros.Para Nomenclatura de Ação, use os tipos de ação descritivos. Prefixe os tipos de ação com o recurso ao qual pertencem, por exemplo, '[Clima] Carregar Clima'. Para cargas de ação, use cargas fortemente tipadas. Você também deve definir interfaces para a carga útil para garantir a segurança de tipo. Outra coisa importante em termos de NgRx é escrever redutores puros. Sempre verifique se os redutores são funções puras e sempre retornam um novo objeto de estado em vez de alterar o estado existente. E mantenha a lógica nos redutores mínimos. Evite efeitos colaterais ou lógica complexa em redutores; delegar aqueles aos efeitos.

Usar seletores para acesso ao estado também é algo a ser considerado. Você pode usar seletores para encapsular a estrutura de estado. Isso ajuda a manter o código do componente limpo e abstrai a forma do estado. Ou você pode compor seletores para criar uma seleção de estado mais complexa. Uma coisa importante a fazer é usar efeitos para lidar com efeitos colaterais como chamadas de API, registro, etc. Isso mantém seus redutores puros e seus componentes limpos. E lembre-se de que você deve sempre lidar com erros nos efeitos. Use as ações apropriadas para gerenciar estados de erro.

Por fim, há várias coisas a serem observadas em termos de teste. Escreva testes de unidade para redutores para garantir que eles retornem o estado correto para determinadas ações. Teste os efeitos para garantir que eles despachem as ações corretas e lidem com os efeitos colaterais adequadamente. Recomendamos usar o 'MockStore' para testar componentes isoladamente do repositório NgRx real.

Vamos demonstrar isso de forma mais vívida com um exemplo de Gerenciamento de Estado Angular:

Imagine que você está criando um aplicativo de previsão do tempo. Nele, você precisa buscar dados sobre atualizações meteorológicas de uma API e exibi-los em várias partes do seu aplicativo. Para fazer isso, você pode criar um WeatherService em Angular que lida com solicitações de API e armazena os dados fornecidos. Este serviço pode ser injetado em diferentes componentes para acessar e exibir as informações meteorológicas.

Embrulhar

Em conclusão, essas técnicas de gerenciamento de estado para aplicativos Angular, usando serviços com RxJS e NgRx, oferecem benefícios e compensações exclusivos que devem ser cuidadosamente considerados com base nos requisitos do projeto e na experiência da equipe. No final, a decisão deve priorizar os requisitos específicos do projeto, as restrições de desenvolvimento e as capacidades da equipe. Ao avaliar cuidadosamente esses fatores, os desenvolvedores podem escolher a abordagem de gerenciamento de estado que melhor se alinha com suas metas e facilita a entrega bem-sucedida do aplicativo Angular.

Ignite UI for Angular library

Solicite uma demonstração