React enforces one-way data flow — data moves from parent to child via props, and children communicate upward through callback functions, making state changes predictable and traceable.
Data flows parent → child via props; children communicate upward via callback functions. The cycle is always: state → render → interaction → callback → state update.
Every piece of state has one owner component — a single source of truth that makes debugging and reasoning about data straightforward.
Angular/Vue offer two-way binding for convenience; React requires explicit handling of every change, trading brevity for predictability.
When siblings need shared data, move state to their closest common ancestor and pass it down as props to both.
When passing props through many levels is tedious, Context API or state management libraries (Redux, Zustand) shorten the path without breaking one-way flow.
Unidirectional (one-way) data flow is a core architectural principle of React. Data always moves in one direction: from parent components down to child components via props. This makes the application's state predictable and easier to debug compared to two-way binding approaches.
In React, a parent component owns state and passes it to children as props. Children can read props but cannot modify them. When a child needs to update data, it calls a callback function that was passed to it as a prop by the parent. The parent's callback updates its state, which triggers a re-render, and the new state flows back down as updated props.
This cycle is always the same: state → props → render → user interaction → callback → state update → re-render. Data flows down, events flow up.
Angular's ngModel and Vue's v-model provide two-way data binding — changes in the view automatically update the model and vice versa. This is convenient for forms but can make data flow harder to trace in large applications. In React, you explicitly handle every state change, which means more code but clearer data ownership.
React forms use "controlled components" to achieve a similar result: the input's value comes from state, and the onChange handler updates state. This is deliberate — you always know where the data is and who changed it.
Every piece of state should have a single owner — one component that is responsible for managing it. Other components that need this data receive it as props. This "single source of truth" principle means:
When two sibling components need the same data, move the state to their closest common ancestor. The parent becomes the single owner and passes data down to both siblings as props. This is React's standard solution for shared state without introducing external state management.
Children communicate upward through callback props. A parent passes a handler function: <SearchBar onSearch={handleSearch} />. The child calls onSearch(query) when the user types. The parent's handler updates state, triggering a re-render with new data flowing down.
For deeply nested components, passing props through many levels ("prop drilling") becomes tedious. Solutions:
Both solutions maintain the unidirectional principle — they just shorten the path data travels.
React's one-way data flow means data always moves parent → child via props, and events always move child → parent via callbacks. This is a deliberate architectural choice that trades convenience (vs two-way binding) for predictability and debuggability. When prop drilling becomes a problem, Context or state management libraries provide alternatives without breaking the unidirectional pattern.
Fun Fact
React's unidirectional data flow was directly inspired by the Flux architecture that Facebook created in 2014. Flux was a response to a bug in Facebook's notification system where the chat badge count and message list would show different numbers — caused by two-way data binding creating conflicting state updates.