Nested
import { Nested } from '@hurum/core'Nested Store는 부모 Store가 자식 Store를 상태 트리의 일부로 구성할 수 있게 해줘요. 다양한 카디널리티를 위한 세 가지 변형이 있어요.
Nested(storeDefinition)
섹션 제목: “Nested(storeDefinition)”단일 Nested 자식 Store를 선언해요. 부모 Store가 생성될 때 정확히 하나의 자식 인스턴스가 생성돼요.
시그니처
섹션 제목: “시그니처”function Nested<TStore>(store: TStore): NestedMarker<TStore, 'single'>매개변수
섹션 제목: “매개변수”| 매개변수 | 타입 | 설명 |
|---|---|---|
store | StoreDefinition | 자식 Store 정의. |
const OrderStore = Store({ state: { orderId: '', transaction: Nested(TransactionStore), },})Nested.array(storeDefinition)
섹션 제목: “Nested.array(storeDefinition)”Nested 자식 Store 배열을 선언해요. 각 항목은 id 필드로 식별돼요. 부모의 raw 상태 배열이 변경되면 항목이 동적으로 추가 및 제거돼요.
시그니처
섹션 제목: “시그니처”Nested.array<TStore>(store: TStore): NestedMarker<TStore, 'array'>매개변수
섹션 제목: “매개변수”| 매개변수 | 타입 | 설명 |
|---|---|---|
store | StoreDefinition | 자식 Store 정의. 각 자식의 상태에 id 필드가 있어야 해요. |
const TodoListStore = Store({ state: { todos: Nested.array(TodoStore), },})자식 Store는 id를 기준으로 raw 상태 배열과 조정돼요:
- 배열의 새 항목은 새 자식 인스턴스를 생성해요.
- 제거된 항목은 해당 자식 인스턴스를 해제해요.
- 기존 항목은 자식 인스턴스를 유지해요 (재생성 없음).
Nested.map(storeDefinition)
섹션 제목: “Nested.map(storeDefinition)”키가 지정된 Nested 자식 Store 맵을 선언해요. 키는 문자열이에요. 부모의 raw 상태 레코드가 변경되면 항목이 동적으로 추가 및 제거돼요.
시그니처
섹션 제목: “시그니처”Nested.map<TStore>(store: TStore): NestedMarker<TStore, 'map'>매개변수
섹션 제목: “매개변수”| 매개변수 | 타입 | 설명 |
|---|---|---|
store | StoreDefinition | 자식 Store 정의. |
const DashboardStore = Store({ state: { panels: Nested.map(PanelStore), },})상태 해석
섹션 제목: “상태 해석”Nested 자식 상태는 getState()가 반환하는 부모의 결합된 상태에 포함돼요:
| Nested 타입 | 상태 형태 |
|---|---|
Nested(Child) | { childKey: ChildState } |
Nested.array(Child) | { childKey: ChildState[] } |
Nested.map(Child) | { childKey: Record<string, ChildState> } |
해석된 상태에는 자식의 raw 상태와 Computed 값이 포함돼요.
scope 접근
섹션 제목: “scope 접근”store.scope로 자식 Store 인스턴스에 접근해요:
const store = OrderStore.create()
// Single: direct instancestore.scope.transaction.send(TransactionIntents.start({ amount: 100 }))
// Array: array of instancesstore.scope.todos.forEach((todo) => { todo.send(TodoIntents.markDone())})
// Map: Map<string, StoreInstance>const panel = store.scope.panels.get('sidebar')panel?.send(PanelIntents.toggle())| Nested 타입 | scope 타입 |
|---|---|
Nested(Child) | StoreInstance |
Nested.array(Child) | StoreInstance[] |
Nested.map(Child) | Map<string, StoreInstance> |
scope는 Executor에서도 context.scope로 사용할 수 있어요.
Event 통신
섹션 제목: “Event 통신”포워딩 (부모에서 자식으로)
섹션 제목: “포워딩 (부모에서 자식으로)”부모 Store가 Event를 적용하면 모든 자식 Store에 전달돼요. 자식에 해당 Event 타입에 대한 on 핸들러가 있으면 자식의 상태가 업데이트돼요.
버블링 (자식에서 부모로)
섹션 제목: “버블링 (자식에서 부모로)”자식 Store가 Event를 발행하면 부모로 버블링돼요. 부모의 Event 구독자 (subscribe('events', cb))가 이를 받고, 부모의 relay 핸들러가 반응할 수 있어요.
relay
섹션 제목: “relay”부모에서 .relay()를 사용해서 자식 Event를 부모 Event로 변환해요:
.relay(TodoEvent.completed, (event, state) => { const allDone = state.todos.every((t) => t.completed) return allDone ? [TodoListEvent.allCompleted()] : []})자식에 대한 의존성 주입
섹션 제목: “자식에 대한 의존성 주입”.childDeps()를 사용해서 부모 의존성을 자식 의존성으로 매핑해요:
const ParentStore = Store({ state: { child: Nested(ChildStore) } }) .deps<{ api: ApiClient }>() .childDeps('child', (parentDeps) => ({ childApi: parentDeps.api.child, }))초기화
섹션 제목: “초기화”Nested.array와 Nested.map의 경우 Store.create({ initialState })로 초기 데이터를 제공해요:
const store = TodoListStore.create({ initialState: { todos: [ { id: '1', title: 'Buy milk', completed: false }, { id: '2', title: 'Write docs', completed: true }, ], },})배열의 각 항목은 해당 항목을 initialState로 사용해서 자식 Store 인스턴스를 생성해요.
참고사항
섹션 제목: “참고사항”Nested.array자식에는id필드가 필수예요. 조정에 필요해요.Nested.array의 자식on핸들러는 id로 가드해야 해요:if (state.id !== payload.id) return state.- 부모 해제 시 모든 Nested 자식에 연쇄적으로 전파돼요.
- relay 깊이는 기본적으로 5로 제한돼요. 깊이 3을 초과하면 개발 경고가 트리거돼요.
- Event 포워딩은 Event당 단방향이에요: 전달된 Event는 다시 버블링되지 않아요.