이 짧은 기사에서는 외부 라이브러리 없이 신호를 사용하여 구성 요소를 구성하는 방법을 보여 드리고 싶습니다. 물론 NgRx와 같은 것들이 우리 코드를 더욱 강력하게 만드는 데 큰 역할을 하겠지만, 간단하게 시작해 보겠습니다!
우선 모든 상태를 신호로 정의합니다.
export class TodoListComponent { todos = signal<Todo[]>([]); }
입력에도 동일하게 적용됩니다! 내 구성 요소에 입력이 필요한 경우 Angular의 새로운 input() 함수를 사용하여 이를 선언하면 신호도 제공됩니다. 그리고 경로 매개변수인 경우 input.required()를 사용합니다.
그런 다음 다른 상태에서 파생될 수 있는 상태를 표시하려면 항상 계산을 사용합니다.
completedTodos = computed(() => this.todos().filter(t => t.completed));
그렇다면 저를 아시는 분이라면 제가 클래스 메서드 내에서 직접 비동기 부작용을 수행하는 것을 얼마나 경멸하는지 아실 겁니다... ?
export class TodoListComponent { todoService = inject(TodoService); toggleTodo(id: string) { this.todoService.toggle(id).subscribe(newTodo => ...); } }
왜 물어보나요? 메서드가 부작용(이 경우 구독 호출)을 직접 시작하는 메서드라면 백 프레셔를 제어할 수 없습니다.
백 프레셔는 다음과 같이 요약할 수 있습니다. 이전 호출이 완료되지 않은 상태에서 사용자가 할 일을 전환하면 어떻게 되나요?
다음과 같은 여러 가지 문제가 있습니다.
RxJS를 알고 계시다면(이 글을 읽고 계시다면 지금쯤은 아실 겁니다!) 첫 번째 문제는 4가지 평면화 연산자(mergeMap, concatMap, switchMap, 배기 맵)를 사용하면 쉽게 해결된다는 것을 아실 것입니다.
그렇다면 RxJS를 꽤 잘 아시는 분이라면 groupBy라는 멋진 연산자로 두 번째 문제를 해결할 수 있다는 사실을 아실 겁니다!
하지만 이 장점을 모두 활용하려면 Observable 소스가 있어야 하므로 메소드가 아닙니다.
주제를 열려 있고(완료되지 않음) 비어 있는 Observable이라고 생각하세요. 맞춤 이벤트를 표현하는 완벽한 도구입니다.
구성요소의 모든 이벤트는 제목으로 표시될 수 있습니다.
export class TodoListComponent { ... toggleTodo$ = new Subject<string>(); deleteTodo$ = new Subject<string>(); addTodo$ = new Subject<void>(); }
그런 다음 템플릿은 메서드를 호출하는 대신 필요할 때 간단히 호출할 수 있습니다. 예를 들면 다음과 같습니다.
<button (click)="deleteTodo$.next(todo.id)">delete</button>
이제 소스가 Observable이므로 연산자를 사용할 수 있습니다. 효과를 만들어 보겠습니다.
저는 구성 요소가 소멸될 때 takeUntilDestroyed() 연산자를 사용하여 효과를 정리할 수 있도록 생성자 내부에 효과를 정의하는 것을 좋아합니다! 예를 들면 다음과 같습니다.
constructor() { this.addTodo$.pipe( concatMap(() => this.todoService.add()) takeUntilDestroyed() ).subscribe(newTodo => this.todos.update(todos => [...todos, newTodo])); }
여기에서는 응답 순서를 유지하여 할 일이 순서대로 추가되도록 concatMap을 사용합니다. 이는 동시 호출이 없음을 의미합니다. 추가 작업에는 완벽하다고 생각하지만 다른 호출에는 잘못된 선택일 수 있습니다. 예를 들어 GET 요청의 경우 사용 사례에 따라 일반적으로 배기 맵 또는 스위치 맵을 사용하는 것이 더 좋습니다.
또한 비관적 업데이트라는 접근 방식을 사용하고 있는데, 이는 내부 상태를 업데이트하기 위해 호출이 끝날 때까지 기다린다는 의미입니다. 이건 개인취향이에요! 즉시 할일을 추가한 다음, API 호출에 오류가 발생하면 catchError를 사용하여 되돌릴 수 있습니다.
그리고 신호와 함께 사용하기 위한 Angular의 실제 효과 기능이 있습니다. 저는 이 기능을 동기화 작업에 사용합니다. 예를 들어 URL에서 매개변수가 변경되면(새 엔터티 ID 참조) 새 엔터티로 양식을 업데이트할 수 있습니다.
// This comes from the router id = input.required<string>(); // Always stores the current invoice information currentInvoice = toSignal(toObservable(this.id).pipe( switchMap(id => this.invoiceService.get(id)) )); constructor() { effect(() => { // Assuming the 2 structures match, every time we browse // to a new invoice, the form gets populated this.form.patchValue(this.currentInvoice()); }) }
이 기술로는 배압을 제어할 수 없습니다. 이런 종류의 경우에는 괜찮지만 이것이 버그 없는 앱을 만들기 위해 여전히 RxJS가 필요한 이유라는 점을 기억하세요. 또는 이러한 복잡성을 내부적으로 추상화하는 또 다른 라이브러리입니다.
우리가 신호로 표현하는 많은 상태는 기술적으로 파생된 비동기 상태로 간주될 수 있습니다. 예를 들어 Todo 목록은 서버에서 파생된 상태로 간주될 수 있습니다.
// Trigger this when you need to refetch the todos fetchTodos$ = new Subject<void>(); todos = toSignal(toObservable(this.fetchTodos$).pipe( switchMap(id => this.todoService.getAll()) ));
이 접근 방식은 새 데이터가 필요할 때 쿼리를 수동으로 무효화하는 TanStack Query와 같은 라이브러리에서 사용하는 접근 방식과 유사합니다. 즉, 변이마다 항상 서버로 이동하게 됩니다.
일부 시나리오에서는 이것이 좋을 수도 있지만 고려해야 할 두 가지 사항이 있습니다.
요컨대 보통은 추천하지 않습니다. 그리고 저는 보통이라고 했어요! :)
이 짧은 기사가 마음에 드셨기를 바랍니다! 요약하자면:
이러한 원칙을 따르면 앱을 유지 관리하기가 훨씬 쉬워질 것이라고 확신합니다!
위 내용은 신호를 사용하여 Angular 구성 요소를 구성하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!