사촌들 잘 놀다: 실험 NgRx 상점과 RTK 조회
RTK 조회도 프레임워크와 무관하며, 이것은 모든 프레임워크에 집적할 수 있다는 것을 의미한다.NgRx는 Redux 기반의 Angular 상태 관리 프레임워크입니다.그래서 그들은 함께 일해야 한다, 그렇지?이 글은 그들이 얼마나 잘 협력했는지, 그리고 내가 실험에서 무엇을 배웠는지 탐구했다.
종속성 설정
Angular 애플리케이션에서 RTK 쿼리를 사용하려면 React 패키지를 설치해야 합니다.나는 유니버설 React와 Redux 패키지를 설치했다.
npm install @reduxjs/toolkit @rtk-incubator/rtk-query react react-dom react-redux
혹은yarn add @reduxjs/toolkit @rtk-incubator/rtk-query react react-dom react-redux
더 많은 유형의 정보를 얻기 위해 @types/react-redux
패키지를 설치했습니다.npm install @types/react-redux --only=dev
혹은yarn add @types/react-redux --dev
청중 설정
RTK 쿼리를 사용하면 스누퍼가 포커스에서 데이터를 다시 추출하고 오프라인으로 다시 연결할 때 데이터를 다시 추출할 수 있습니다.이것은
setupListeners
함수를 사용하여 Store.dispatch
방법을 그에게 전달하는 것과 관련이 있다.import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Store, StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { setupListeners } from '@rtk-incubator/rtk-query';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { environment } from '../environments/environment';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
StoreModule.forRoot({}),
EffectsModule.forRoot([]),
StoreDevtoolsModule.instrument({ maxAge: 25, logOnly: environment.production })
],
bootstrap: [AppComponent]
})
export class AppModule {
constructor(store: Store) {
setupListeners(store.dispatch.bind(store));
}
}
주의: 전체 실험 과정에서 Store 실례의 작용역을 잃지 않기 위해 나는 반드시 .bind
문법을 사용해야 한다.제품 라인 생성
기능 페이지에 대해, 나는 로드가 지연되는 제품 모듈과 제품 구성 요소를 생성했다.
ng g module products --route products --module app
API 서비스 설정
RTK 쿼리에서는 상태 슬라이스의 모든 데이터 가져오기 및 상태 작업을 처리하기 위해 API 서비스를 만듭니다.이러한 서로 다른 방법은 생성된 서비스를 통해
endpoints
으로 정의된다.나는 Product
인터페이스를 정의했고 productsApi
함수를 사용하여 createApi
인터페이스를 정의했다.import { createSelector } from '@reduxjs/toolkit';
import { createApi, fetchBaseQuery } from '@rtk-incubator/rtk-query';
export interface Product {
id: number;
name: string;
}
export const productsApi = createApi({
reducerPath: 'products',
entityTypes: ['Products'],
baseQuery: fetchBaseQuery({ baseUrl: 'http://localhost:3000/' }),
endpoints: (builder) => ({
getProducts: builder.query<Product[], string>({
query: () => 'products',
provides: (result) => result.map(({ id }) => ({ type: 'Products', id }))
}),
addProduct: builder.mutation<Product, Partial<Product>>({
query: (body) => ({
url: `products`,
method: 'POST',
body,
}),
invalidates: ['Products']
}),
}),
});
참고: 이 예에서 사용된 json-server
패키지에 빠른 API를 설치했습니다.엔드포인트 사용 빌더 모드는
http://localhost:3000/products
에 API URL 경로, API 조회, 돌연변이 및 캐시 실효를 제공합니다.React에서 갈고리로 추상적인 Products
의 용법을 제공했는데 이 갈고리는 조작과 선택기를 포함하지만 React 이외의 용법을 제공한다.endpoints
대상은 productsApi
이라는 기능 키와 reducerPath
함수 자체를 포함한 다른 속성도 포함한다.행동을 정의하다
reducer
은 productsApi
속성에 정의된 방법을 포함하는데 이런 방법은 본질적으로 동작 창설자이다.export const loadProducts = () => productsApi.endpoints.getProducts.initiate('');
export const addProduct = (product: Product) => productsApi.endpoints.addProduct.initiate(product);
endPoints
방법은 전통적인 동작 창설자처럼 보이는 내용을 되돌려주지만 실제로는 RTK 조회 중간부품에서 사용하는'Thunks'다.선택기 정의
선택기도 생성된
initiate
의 정의를 사용한다.endpoints
은 전체 상태 자체로 돌아간다.이는 NgRx 스토어의 getProducts
기능과 원활하게 통합되어 제품에 대한 또 다른 선택기를 구축합니다.export const selectProductsState = productsApi.endpoints.getProducts.select('');
export const selectAllProducts = createSelector(
selectProductsState,
state => state.data || []
);
RTK 쿼리는 createSelector
, isLoading
, isError
및 isSuccess
등의 다른 정보를 얻기 위한 선택기를 제공합니다.악극을 다루다
내가 만난 첫 번째 장애는 NgRx 상점과 Redux가 의견 차이를 보이기 시작했다는 것이다.Redux 스토리지는 상태 스냅샷을 가져오고 Redux Thunk를 처리하는 동기화 방법을 제공합니다.Redux Thunk 문서
"Thunks는 기본 Redux 부작용 논리의 추천 중간부품으로 메모리에 접근해야 하는 복잡한 동기화 논리와 간단한 비동기 논리, 예를 들어 AJAX 요청 등이 포함됩니다."
이 API를 복제하기 위해
ThunkService
을 만들었습니다. 이것은 Redux 중간부품을 사용하는 데 필요한 같은 API를 제공합니다.import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { MiddlewareAPI, ThunkAction } from '@reduxjs/toolkit';
import { SubscriptionLike } from 'rxjs';
import { take } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class ThunkService {
constructor(private store: Store) {}
getState() {
let state: object;
this.store.pipe(take(1)).subscribe((res) => {
state = res;
});
return state;
}
dispatch(thunkAction: ThunkAction<any, any, any, any>): SubscriptionLike {
return thunkAction(
(thunk: ThunkAction<any, any, any, any>) =>
thunk(this.store.dispatch.bind(this.store), this.getState.bind(this), undefined),
this.getState.bind(this),
undefined
);
}
runThunk(thunk: ThunkAction<any, any, any, any>) {
return thunk(
this.store.dispatch.bind(this.store),
this.getState.bind(this),
undefined
);
}
middlewareApi(): MiddlewareAPI {
return {
dispatch: this.runThunk.bind(this),
getState: this.getState.bind(this),
};
}
}
getState
방법은 저장된 스냅샷을 제공하고 dispatch
방법은 제공된'thunks'비동기 스케줄링 조작을 허용한다.연결 미들웨어
Redux 중간부품을 사용하기 위해서 상점에 연결해야 합니다.나는 마침내 메타 리듀서를 사용했다.기본적으로, 메타데이터 약간기는 하나의 Reducer 함수를 수신하고 다른 Reducer 함수를 되돌려주는 방법일 뿐, 이 함수는 NgRx 저장소에서 '중간부품' 을 충당한다.
InjectionToken
을 사용하지 않으면 의존항 주입에 접근할 수 없기 때문에 저는 제품 기능 상태 설정을 위해 InjectionToken
을 만들었습니다.export const PRODUCTS_FEATURE_CONFIG_TOKEN = new InjectionToken<StoreConfig<any>>('Products Feature Config');
그리고 ThunkService
을 사용하려면factory 함수를 만들었고 productsApi
이 제공하는 중간부품을 사용하려면metareducer를 만들었습니다.등록 후 이 메타데이터 축소기는 products
상태 슬라이스에만 적용됩니다.export function productsMiddleware(dispatcher: ThunkService) {
return function(reducer: ActionReducer<any>): ActionReducer<any> {
return function(state, action) {
const next = productsApi.middleware(dispatcher.middlewareApi());
const nextState = next(action => reducer(state, action));
return nextState(action);
};
}
}
productsMiddleware
함수로 정의된 원약간을 되돌려주는 공장 함수가 필요합니다.export function getProductsFeatureConfig(thunkService: ThunkService): StoreConfig<any> {
return {
metaReducers: [productsMiddleware(thunkService)]
};
}
제품 기능 구성을 등록하려면 공급업체가 필요합니다.export function provideProductsFeatureConfig() {
return [
{
provide: PRODUCTS_FEATURE_CONFIG_TOKEN,
deps: [ThunkService],
useFactory: getProductsFeatureConfig,
}
];
}
등록 기능 상태
기능 상태는 setup이고 익숙한
ProductsModule
문법을 사용하여 StoreModule.forFeature()
에 등록합니다.import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { StoreModule } from '@ngrx/store';
import { ProductsRoutingModule } from './products-routing.module';
import { ProductsComponent } from './products.component';
import { PRODUCTS_FEATURE_CONFIG_TOKEN, productsApi, provideProductsFeatureConfig } from '../services/products';
@NgModule({
declarations: [ProductsComponent],
imports: [
CommonModule,
ReactiveFormsModule,
ProductsRoutingModule,
StoreModule.forFeature(productsApi.reducerPath, productsApi.reducer, PRODUCTS_FEATURE_CONFIG_TOKEN)
],
providers: [
provideProductsFeatureConfig()
],
})
export class ProductsModule { }
productsApi.reducerPath
은 API 서비스에서 정의한 products
으로 Reducer 함수를 완전히 생성하고metareducer를 등록할 때 PRODUCTS_FEATURE_CONFIG_TOKEN
영패와 provideProductsFeatureConfig
방법으로 등록 공급자가 방문 의존항으로 주입한다.제품 추가
Product
을 추가하기 위해서, 나는 리액션 폼을 사용하여 폼을 제출한 후에 제품을 출력하기 위해 작은 폼 구성 요소를 신속하게 조립했다.import { Component, OnInit, EventEmitter, Output } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
@Component({
selector: 'app-product-form',
template: `
<form [formGroup]="productForm" (ngSubmit)="addProduct()">
<div>
Name: <input formControlName="name" type="text">
</div>
<div>
Description: <input formControlName="description" type="text">
</div>
<button type="submit">Add Product</button>
</form>
`
})
export class ProductFormComponent implements OnInit {
productForm = new FormGroup({
name: new FormControl(''),
description: new FormControl('')
});
@Output() submitted = new EventEmitter();
constructor() { }
ngOnInit(): void {
}
addProduct() {
this.submitted.emit(this.productForm.value);
}
}
ProductsFormComponent
템플릿에서 사용할 수 있도록 ProductsComponent
을 등록했습니다.import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { StoreModule } from '@ngrx/store';
import { ProductsRoutingModule } from './products-routing.module';
import { ProductsComponent } from './products.component';
import { ProductFormComponent } from './product-form.component';
import { PRODUCTS_FEATURE_CONFIG_TOKEN, productsApi, provideProductsFeatureConfig } from '../services/products';
@NgModule({
declarations: [ProductsComponent, ProductFormComponent],
imports: [
CommonModule,
ReactiveFormsModule,
ProductsRoutingModule,
StoreModule.forFeature(productsApi.reducerPath, productsApi.reducer, PRODUCTS_FEATURE_CONFIG_TOKEN)
],
providers: [
provideProductsFeatureConfig()
],
})
export class ProductsModule { }
제품 가져오기 및 추가
이 상태가 모두 연결되어 있기 때문에
ProductsComponent
에서 이 상태를 사용했습니다.나는 app-product-form
을 사용해서 상점에서 고른 제품을 열거했다.RTK 쿼리 중간부품과 함께 사용하는 Action Thunks의 스케줄러로 ThunkService
을 주입했습니다.import { Component, OnDestroy, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { SubscriptionLike } from 'rxjs';
import * as uuid from 'uuid';
import { Product } from '../models/product';
import { addProduct, loadProducts, selectAllProducts } from '../services/products';
import { ThunkService } from '../services/thunk.service';
@Component({
selector: 'app-products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit, OnDestroy {
products$ = this.store.select(selectAllProducts);
loadSub$: SubscriptionLike;
addSub$: SubscriptionLike;
constructor(private store: Store, private dispatcher: ThunkService) { }
ngOnInit(): void {
this.loadSub$ = this.dispatcher.dispatch(loadProducts());
}
onProductAdded(product: Product) {
this.addSub$ = this.dispatcher.dispatch(addProduct({id: uuid.v4(), ...product}));
}
ngOnDestroy() {
this.loadSub$.unsubscribe();
this.addSub$.unsubscribe();
}
}
products
선택기를 사용하여 selectAllProducts
10dispatcher
RTK 쿼리에서 제공하는 동작 Thunk을 전송합니다.만약 당신이 주의하지 않는다면, 나는 HttpClientModule
또는 NgRx Effects을 사용하여 어떠한 데이터를 얻거나 부작용을 일으키지 않을 것입니다.이러한 모든 동작은 RTK 조회를 통해 내부적으로 처리됩니다.Redux 개발 도구의 모든 변경 사항을 볼 수 있습니다.첫인상
당신은 어떻게 생각합니까?Angular 애플리케이션에서 RTK 쿼리를 사용하시겠습니까?아래에 글을 남기거나 트위터에 연락 주세요.
참고: Saul Moro는 Angular, NgRx 및 RTK 질의에 대한 갈고리 스타일 라이브러리를 작성했습니다.재구매 계약 보기:
https://github.com/SaulMoro/ngrx-rtk-query
Reference
이 문제에 관하여(사촌들 잘 놀다: 실험 NgRx 상점과 RTK 조회), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/brandontroberts/cousins-playing-nicely-experimenting-with-ngrx-store-and-rtk-query-25f4텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)