Hello! Welcome to the final lesson in our module on React's rendering model.
In our last lesson, we took a deep dive into the component lifecycle, breaking it down into the Mounting, Updating, and Unmounting phases. We established that a component enters the "Updating" phase whenever its state or props change.
Today, we'll focus on that second trigger: props. This lesson addresses the learning outcome: Explain the principle of unidirectional data flow and how props are passed down the component tree.
We will explore one of React's most fundamental architectural principles. You will learn:
- What unidirectional data flow is and why React chose this model over alternatives like two-way data binding.
- The mechanics of passing data from parent to child components using
props. - The established pattern for child components to communicate back to their parents, known as "lifting state up."
- The potential challenges this pattern introduces, such as "prop drilling."
This lesson connects directly to our previous discussion on lifecycles. Understanding how data flows is key to understanding why and when the component update lifecycle is triggered throughout your application.
The Principle: Unidirectional Data Flow
At its core, React is built around a simple and predictable concept: data flows in one direction. This is often described as "top-down" data flow.

This means that data, which is held as state in a component, is passed down to its children as props. A child component receives data from its parent, but it cannot directly modify the data of its parent or pass data back up.
This might seem restrictive, but it brings immense clarity. When you're debugging, you know that a component's data comes either from its own internal state or from props passed by its parent. You don't have to hunt for changes happening from unpredictable places in the component tree.
This is in contrast to two-way data binding, a pattern used in frameworks like the original AngularJS, where changes in a child component could automatically update the parent's state, and vice-versa. While this can be convenient for simple forms, it often leads to a complex web of dependencies that is difficult to understand and debug in larger applications.
Why Two-Way Data Binding Is(n't) Bad
To understand why React's model is so beneficial, it helps to understand the problems it solves. This article provides a concise analysis of two-way data binding and its potential for creating complexity, then contrasts it with the simplicity of unidirectional flow.
Read the sections 'Two-way data binding?', 'Two-way data binding hell', and 'Unidirectional Data Flow'. Focus on the core argument: unidirectional flow caps the cognitive complexity required to understand state changes. The formula 'old state + action = new state' is the essence of this predictability.
The key takeaway is that unidirectional flow makes the cause-and-effect of data changes explicit and traceable.

The Mechanism: Passing Data with props
The vehicle for unidirectional data flow is the props object. "Props" is short for "properties." You pass data to a child component by defining attributes on its JSX tag, much like you would with standard HTML attributes.
Let's examine how this works.
This article by Robin Wieruch provides a thorough, code-driven walkthrough of using props. It covers everything from basic syntax to the relationship between state and props.
Please read the following sections: 'React Component Props by Example': This covers the basic syntax of passing and receiving props. 'React Props are just the Communication Channel' (the short paragraph before the 'vs. State' section): This reinforces that props are immutable (read-only) for the receiving component. 'React Props vs. State': This is a crucial section. It explains how a parent's state becomes a child's props and how a state change in the parent triggers a re-render that passes down new props.
To summarize the key points from that reading:
- Passing Props: You pass data by adding custom attributes to a component's JSX tag:
<ChildComponent user={userData} isLoggedIn={true} />. - Receiving Props: A function component receives a single argument, the
propsobject. You can access the data viaprops.useror, more commonly, by destructuring:const ChildComponent = ({ user, isLoggedIn }) => { ... }. - Props are Read-Only: A component must never attempt to modify its own
props. They are immutable from the child's perspective. - State becomes Props: A parent component often passes its own state down to a child. When the parent's
setStatefunction is called, the parent re-renders, and the child receives the new state value as its new props. This is the link that triggers the "update" lifecycle throughout the tree.
The Loop: Communicating Up with Callbacks
If data only flows down, how does a child component inform a parent of an event, like a button click or form submission?
The answer doesn't break the rule of unidirectional flow. Instead, it uses props to its advantage. Since you can pass any JavaScript type as a prop, you can also pass a function.
This pattern is called "Lifting State Up."
- A parent component that owns the state also defines the function that modifies that state (e.g.,
handleUpdate). - The parent passes this function down to the child component as a prop (e.g.,
<ChildComponent onUpdate={handleUpdate} />). - The child component receives
onUpdateas a prop and can call it whenever needed (e.g., in anonClickhandler). - When the child calls the
onUpdatefunction, it's actually executing thehandleUpdatefunction defined in the parent. - The parent's state is updated, it re-renders, and the new state flows back down to all children as props.
This creates a complete data loop: data flows down, and actions (via callbacks) flow up.
React Typescript 2023 - 8. Data Flow
Let's watch a practical demonstration of this refactoring. This video walks through moving state from a 'dumb' child component to a 'smart' parent component, implementing the 'Lifting State Up' pattern.
Watch from the beginning to 05:56. The video will: Introduce the 'smart' vs. 'dumb' component concept and the 'data down, events up' model. Show the code refactoring process of moving state and logic from a child to a parent. Demonstrate passing both state values and event handlers down as props. (Bonus) It uses TypeScript, showing how to define the types for these props, which aligns with modern development practices you're familiar with.
To solidify this concept, let's return to the article from Robin Wieruch, which explains the same pattern textually.
This section of the article provides another excellent explanation of lifting state up.
Read the section 'How to pass Props from child to parent Component'. It clearly demonstrates how a broken example is fixed by moving state to the parent and passing a callback function down.
The Challenge: Prop Drilling
Unidirectional data flow is powerful, but it has a well-known drawback. What happens when a component deep in the tree needs data from a top-level component?
The data must be passed down as props through every single intermediate component, even if those components don't need or use the data themselves. This is called prop drilling.
Senior-Level Understanding of React Portals
This short clip explains the concept of prop drilling and the coupling it can introduce.
Watch from 02:27 to 03:20. The video explains how unidirectional data flow naturally leads to prop drilling when you need to pass data or functions to deeply nested components.
Prop drilling can make your components less reusable and more tightly coupled to their context. While it's perfectly fine for a few levels, it can become cumbersome in large applications. This is the primary problem that React's Context API and third-party state management libraries like Redux are designed to solve. We will explore those solutions in Module 11.
Conclusion
In this lesson, we established the fundamental principles of how data moves in a React application. This is the final piece of the puzzle for understanding React's core rendering model.
Key Takeaways:
- Unidirectional Data Flow: Data flows in one direction, from parent components to child components. This makes application state predictable and easier to debug.
- Props: Data and callback functions are passed from parent to child via the
propsobject. Props are read-only from the child's perspective. - Lifting State Up: To communicate upwards, a parent passes a callback function to a child. The child calls this function to trigger a state change in the parent. This completes the "data down, actions up" cycle.
- Prop Drilling: A natural consequence of this pattern is that props sometimes need to be passed through many intermediate components, which can lead to code coupling.
What's Next?
Congratulations on completing the module on React's Rendering Model! You now have a solid conceptual foundation for how React works under the hood—from transpiling JSX, to reconciliation and keys, to the component lifecycle, and finally, to data flow.
In our next module, Mastering React Hooks, we will transition from theory to a deeper practical understanding of the tools you use to build components. We'll begin by examining the Rules of Hooks—the constraints that allow hooks to work their magic—and then take a deep dive into the most fundamental hook of all: useState.