7.7: Designing Component Hierarchy


One of the most difficult, confusing aspects of building a React app is deciding what components are responsible for what, and where we store data. We'll look at principles of React component architecture through examples.
Note that some React best practices are what is left out, i.e., state is not created unnecessarily, data is only controlled top-down, components functionally calculate values and states where possible, etc.

E-Commerce App Example

Pre-example: DB Setup (follow the cheatsheet)

  1. 1.
    Ensure that your postgresql server is running.
  2. 2.
    In your VS Code, remember to npm install your packages. pg and sequelize-cli are already called upon.
  3. 3.
    Go to config/config.js and change your username.
  4. 4.
    Terminal Command: npx sequelize db:create
  5. 5.
    Terminal Command: npx sequelize db:migrate
  6. 6.
    Terminal Command: npx sequelize db:seed:all


  1. 1.
    Our example is a full-stack e-commerce app. The app begins empty.
  2. 2.
    When the user clicks a button, the app requests a list of all available items from the server.
  3. 3.
    When the user clicks on any item in the list a detail view appears.
  4. 4.
    When the user clicks the Add to Cart button, the cart is displayed with all the items. In the cart the total for all items is calculated.

"App-Level" (or "Parent-Level") Data vs "Component-Level" Data

App-Level Data

Because the data about what items are in the list is needed throughout the app, we put the items array at the "app level", in the App component. This is the same for all the other data we need throughout the app, such as which item is currently selected, and the array of items in the cart.
const [items, setItems] = useState([]);
const [cart, setCart] = useState([]);
const [selectedItemIndex, setSelectedItem] = useState();

Component-Level Data

There are a few pieces of data that are not needed at the app level, such as the GST calculated on all the items in the cart and the quantity selected in the detail view.
const subTotal = items.reduce(
(acc, item) => Number(acc) + Number(item.price),
const gst = subTotal * 0.07;
const total = subTotal + gst;

Data Flows

Modifying Data in Parent Components via Prop Functions

Similar to Module 7.6: Passing Data Between Sibling Components, when a child component needs to manipulate data at the app level we pass a "prop function" down into the child component, which sends the parent necessary data via the prop function when the user performs an action.
For example, when the user clicks to add an item to cart, we send that item's index to App via a prop function to add that item to the cart array stored in app state. We format the data (to add quantity) in the prop function before modifying cart.


The following examples demonstrate the same concepts as in Module 7.6: Passing Data Between Sibling Components.
  1. 3.
    The setting of the cart array sets off the rendering of the Cart component and items in the cart.