--- id: wiki-2026-0508-one-way-data-flow title: One-way Data Flow category: 10_Wiki/Topics status: verified canonical_id: self aliases: [One-way Data Flow, Unidirectional Data Flow, Top-down Props] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [react, frontend, architecture, data-flow, state-management] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: TypeScript framework: React --- # One-way Data Flow ## 매 한 줄 > **"매 state 매 down, event 매 up"**. Unidirectional data flow 매 React (Flux 영감) 의 core mental model — parent props 으로 child 에 데이터 전달, child callback 으로 parent 에 event 통보. 매 reasoning 의 단순화, debugging 의 traceability, time-travel 의 가능성. Vue/Solid/Svelte 도 매 동일 원칙. ## 매 핵심 ### 매 핵심 규칙 - State 매 single owner (component or store). - Owner 만 mutate 가능. - Child 매 read-only props 만 받음. - Child 의 변경 요청 매 callback / event / dispatch. ### 매 왜 unidirectional - **Predictability**: state 변경 source 매 명확. - **Debugging**: render output 매 props 의 함수. - **Time-travel**: state snapshot 만 으로 UI 재현. - **Concurrency**: 매 React Concurrent 매 mutable 2-way 매 deadlock-prone. ### 매 응용 1. React props/callback 패턴. 2. Redux / Zustand / Jotai store dispatch. 3. Vue `props down, emit up`. 4. Form: lift state up. 5. Event sourcing / CQRS frontend. ## 💻 패턴 ### Lifting state up (React) ```tsx function Parent() { const [name, setName] = useState(""); return ( <> ); } function NameInput({ value, onChange }: { value: string; onChange: (v: string) => void }) { return onChange(e.target.value)} />; } function Greeting({ name }: { name: string }) { return

Hello, {name || "stranger"}!

; } ``` ### Reducer (action up, state down) ```tsx type Action = { type: "add"; item: string } | { type: "remove"; idx: number }; type State = { items: string[] }; function reducer(s: State, a: Action): State { switch (a.type) { case "add": return { items: [...s.items, a.item] }; case "remove": return { items: s.items.filter((_, i) => i !== a.idx) }; } } function TodoApp() { const [state, dispatch] = useReducer(reducer, { items: [] }); return ( <> dispatch({ type: "add", item })} /> dispatch({ type: "remove", idx })} /> ); } ``` ### Zustand (store as single source) ```ts import { create } from "zustand"; interface CartStore { items: { id: string; qty: number }[]; add: (id: string) => void; remove: (id: string) => void; } export const useCart = create((set) => ({ items: [], add: (id) => set((s) => ({ items: [...s.items, { id, qty: 1 }] })), remove: (id) => set((s) => ({ items: s.items.filter((i) => i.id !== id) })), })); function ProductCard({ id }: { id: string }) { const add = useCart((s) => s.add); return ; } ``` ### Vue 3 props down + emit up ```vue ``` ### Server-driven (React Query + URL state) ```tsx import { useQuery } from "@tanstack/react-query"; import { useSearchParams } from "react-router"; function ProductList() { const [params, setParams] = useSearchParams(); const category = params.get("category") ?? "all"; const { data } = useQuery({ queryKey: ["products", category], queryFn: () => fetch(`/api/products?cat=${category}`).then((r) => r.json()), }); return ( <> setParams({ category: v })} /> {data?.map((p) => )} ); } ``` ### Forbidden 2-way leak (anti-pattern shown) ```tsx // ❌ child mutates parent's prop directly via mutable ref function BadChild({ obj }: { obj: { count: number } }) { return ; // parent never re-renders } ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | 2-3 components share state | Lift state up | | Cross-tree state | Context / Zustand / Redux | | Server data | React Query / SWR (single source) | | URL state | useSearchParams / Next router | | Form-heavy local | useReducer + dispatch | | Vue | props + emit (or Pinia) | **기본값**: state 매 highest common ancestor 또는 매 store. 매 props down + callback up. ## 🔗 Graph - 부모: [[React]] - 변형: [[프론트엔드_및_UIUX_표준|Redux]] · [[Zustand]] · [[Pinia]] - 응용: [[useReducer]] · [[Event-Sourcing]] - Adjacent: [[Reactive-Streams]] · [[Signals]] ## 🤖 LLM 활용 **언제**: React/Vue/Solid component design, form lifting, store architecture. **언제 X**: 매 highly local input (e.g. simple text field) — 매 controlled-input over-engineering 회피, uncontrolled OK. ## ❌ 안티패턴 - **Mutate props in child**: 매 silent re-render miss. - **Shared mutable ref**: 매 React diff 의 invariant 위반. - **Two-way binding in big tree**: 매 cycle / cascade. - **Prop drilling 10층**: 매 Context / store 으로 cut. - **Local form state in 매 sync 매 server**: 매 stale conflict — server 매 source of truth + optimistic update. ## 🧪 검증 / 중복 - Verified (React docs — Sharing State Between Components, Vue docs — Component Basics, Redux principles). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — lifting/reducer/Zustand/Vue emit/server-driven 패턴 |