- Data flows only one way: From parent to child components (down the component hierarchy)
- Component restricted scope: A component can only update its own state
Being able to build efficient React applications demand a practical understanding of these two rules and a thinking paradigm which embraces React philosophy. The following is an idea of how to think the React way.
Start From A Mock
Start by exploring your app’s mock and JSON API. The mock will help you visualize the finished application, understand the different states in play and the JSON API will give you an understanding of the data model’s structure (check out a sample mock and a JSON API).
Never start developing your app without fully studying the mock!
Step 1: Break the UI Into A component Hierarchy
Step 1-1: Uncover components hidden in your mock
Here is how to do it:
- Visually split the mock into logical sections, try to isolate the parts that play one role only. I usually print the mock on paper and draw colored boxes around potential components
- Name these components with logical names expressing what roles they play (this is great for code clarity)
- Make sure each component represents exactly one piece of your data model (you don’t want to have components that serve no purpose)
Always break the UI into components that represent exactly one piece of your data model
Step 1-2: Organize your components into hierarchy
Hierarchy is crucial in react. Its one-way data flow nature forces data to flow only from parents to children components (contrary to AngularJS which uses a two-way data binding flow), so establishing a hierarchy in the planning process help foresee possible channels of data flow. Here is a sample component hierarchy and its implications (in terms of data flow):
- Main Component
- Component 2 (can receive data from Main Component)
- Component 3 (can receive data from Component 2)
- Component 4 (can receive data from Component 2)
- Component 5 (can receive data from Component 4)
- Component 6 (can receive data from Main Component)
- Component 7 (can receive data from Component 6)
- Component 2 (can receive data from Main Component)
Step2: I Build A Static Version Of The App (In React)
Step 2-1: Mind your direction
Component-based applications are built in two directions:
- top-down: You start building the highest components in the hierarchy, then progress down to lower components (this is mostly done in smaller projects)
- bottom-up: You start building the lowest hierarchical components, then move up to the highest ones (done in larger projects)
Step 2-2: No interactivity At First
Start by building a static version of your application, don’t worry about interactivity, focus only on building reusable components. During this process, only use props instead of states (“State” is for interactivity, for the type of data that changes over time).
Do not use state when building the static version of your app. State is reserved for interactivity, for data that changes over time.
Step3: Identify the minimal representation of UI state
Here you need to find out the state of the application. Here is some characteristic of the state:
- It will be subject to constant changes over time
- It is unique for the entire application
- It is used to compute everything else on demand
To find out what is the application’s state, look at each piece of data and ask yourself three questions:
- Is this data passed from a parent via props? (if yes, it might not be a state)
- Is this data the result of a computation? (if yes, it might not be a state)
- Does this data remain unchanged over time? (if yes, it might not be a state)
As a great example of a state, we can think about the array of elements in a TODO list because:
- It changes over time (adding or removing new items)
- It is used to compute other data (number of items, filtered set of items, …)
The state is the minimal representation of the UI dynamism. It is the fondation on which stands the entire application’s interactivity!
Step4: Figure Out Where The State Should Live
Now we need to figure out which component should own the state. React is a one-way data flow framework (one-way data binding), data travels down the component hierarchy, so the state should always live in a component which is a direct parent of the components where it will be used.
Here is a simple rule to find out where a state should live:
- Identify every component that renders something based on that state
- Find a common owner component (a single component above all the components that need the state in the hierarchy)
- Either the common owner or another component higher up in the hierarchy should own the state
- If you can’t find a component where it makes sense to own the state, create a new component simply for holding the state and add it somewhere in the hierarchy above the common owner component
So let’s suppose you have 2 components which use the same state, you need to find them a common ancestor, initialize your state and pass the states through props.
See how this is applied in a data table case study.
Step5: Add Inverse Data Flow
Remember that our data flows only one way in React, we need to make sure it flows both ways. So that a state model leaving in a parent component can be notified of a change happening in a children component. Here is how we do this:
- From the parent component: wrap a callback around the function that set the state
- Pass that callback to the child component via “props”
- Inside the child component: Call the parent’s callback function from an event listener function
- The event listener being called within the child component will create a chain reaction that will climb up to the parent component, update the state and finally refresh the entire app
Thinking in React involves the following steps:
- Breaking the UI into a components hierarchy
- Identifying the components hidden in the mock
- Breaking these components into hierarchy
- Building a static version of the app (in React)
- Not worrying (at first) about adding any interactivity
- Choosing your building direction (up-down or down-up)
- Identifying the minimal representation of the UI state
- Figuring out where the state should live
- Adding an inverse data flow
We’ve just been through the thinking process of building an application in React. This process might be a little bit longer than usual, the amount of code also, but this is a small price to pay compared to the rewards of code clarity, understanding, and great performance. For a practical application of this process please read my post on building a data table with React.
Inspiration: Thinking in React