DEV Community

Cover image for Thinking in React Reference
Liu Yongliang
Liu Yongliang

Posted on • Edited on

Thinking in React Reference

Thinking in React is an article from the official React Doc that talks about the development process for a typical React App

React is, in our opinion, the premier way to build big, fast Web apps with JavaScript. It has scaled very well for us at Facebook and Instagram.

I will be making a simple React App to illustrate the process.

Demo below:
A React App that reminds you of the steps in developing a React App...

Step 0: Start With A Mock

The first thing to do is to have some sort of mental picture of how the App is going to look like. Preferably, have a sketch/mock of the UI.

This is what I came up with:
A mock-up of the web app design

Secondly, imagine what data from an API/data source will look like. Given that I already have the steps involved in developing a React App and I have it in the following format:

const data = [
  {
    heading: "Start With A Mock",
    content: "Any input from user. Front-end in its plain form",
  },
  {
    heading: "Break The UI Into A Component Hierarchy",
    content:
      "Draw Boxes.Identify components(Single Responsibility Principle)
.Arrange into hierarchy",
  },
  {
    heading: "Build A Static Version",
    content:
      "Don't use state at all, only use Props.Reuse components.
Top down/Bottom up to you.Pass your data model to 
the top of the hierarchy",
  },
  {
    heading: "Identify The Minimal Representation of UI State",
    content:
      "Keep only the absolute minimal and compute 
everything else on-demand.Is it passed in from a parent via props?
If so, it probably isn't state.
Does it remain unchanged over time? If so, it probably isn’t state.
Can you compute it based on any other state or props in your component? 
If so, it isn’t state",
  },
  {
    heading: "Identify Where Your State Should Live",
    content:
      "Identify every component that renders something 
based on that state.
Find a common owner component(above all other components).
Create a wrapper component above components if necessary",
  },
  {
    heading: "Add Inverse Data Flow",
    content:
      "Pass state changing callbacks from state owner 
to relevant child component",
  },
];
Enter fullscreen mode Exit fullscreen mode

Step 1: Break The UI Into A Component Hierarchy

I started with identifying components from my UI.

  1. ReferenceTable: container
  2. StepNumberBar: reflect the current step number
  3. Explanation: displays the current step details
  4. KeyList: displays a list of bullet points
  5. NavigationRow: contains navigation buttons
  6. MoveStepButton: displays a button

Now that I identified the components in our mock, I arranged them into a hierarchy.

  • ReferenceTable
    • StepNumberBar
    • Explanation
      • KeyList
    • NavigationRow
      • MoveStepButton

Step 2: Build A Static Version in React

Now I started building components top down. This process was a lot of debugging & frustration. Working with sample data helps. Also, focus on getting the skeleton out before you start polishing the components with CSS. But, do throw in some of the centering/alignment CSS along the way so that the App will start to take its shape. No state management at all at this stage.

Some of the basic functional components as follows:

function StepNumberBar({ total, current }) {
  return (
    <div className="stepNumberBar">
      {Array(total)
        .fill(null)
        .map((value, index) => (
          <span
            id={index}
            key={index}
            className={current === index ? "active" : "inactive"}
          >
            {index}
          </span>
        ))}
    </div>
  );
}

function KeyList({ content }) {
  var itemsArr = content.split(".");
  return (
    <ul>
      {itemsArr.map((item, index) => (
        <li key={index}>{item}</li>
      ))}
    </ul>
  );
}

function Explanation({ heading, content }) {
  return (
    <div className="explanation">
      <h2>{heading}</h2>
      <KeyList content={content} />
    </div>
  );
}
function NavigationRow() {
  return (
    <div className="buttons">
      <MoveStepButton direction="prev" />
      <MoveStepButton direction="next" />
    </div>
  );
}

function MoveStepButton({ direction }) {
  return direction === "prev" ? (
    <button>
      PREV
    </button>
  ) : (
    <button>
      NEXT
    </button>
  );
}

function ReferenceTable({ detail }) {
  return (
    <>
      <StepNumberBar total={detail.length} current={currPage} />
      <Explanation
        heading={detail[currPage].heading}
        content={detail[currPage].content}
      />
      <NavigationRow />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Identify The Minimal (but complete) Representation Of UI State

Now, thinking of all of the relevant data, I have:

  • The step number
  • The step detail

Going through the three questions for each piece of data:

  1. The step number changes when users navigate from one step to another. Hence it is probably state.
  2. The step detail is passed as props, does not change over time, so that's probably not state.

I ended up with only one state that I manipulated with the useState hook:

  const [currPage, updatePage] = useState(0);
Enter fullscreen mode Exit fullscreen mode

Step 4: Identify Where Your State Should Live

Given that the step number needs to be displayed in StepNumberBar and also updated by the buttons in NavigationRow, the state needs to live in one component higher: ReferenceTable.

Step 5: Add Inverse Data Flow

Since components should only update their own state, I passed the update function from ReferenceTable to MoveStepButton that will fire whenever the state should be updated. I used the onClick event to update the state. I also added some cool CSS effect that you can explore here.

Partial code as follows:

function ReferenceTable({ detail }) {
  const [currPage, updatePage] = useState(0);
  return (
    <>
      <StepNumberBar total={detail.length} current={currPage} />
      <Explanation
        heading={detail[currPage].heading}
        content={detail[currPage].content}
      />
      <NavigationRow updatePage={updatePage} />
    </>
  );
}
function NavigationRow({ updatePage }) {
  return (
    <div className="buttons">
      <MoveStepButton updatePage={updatePage} direction="prev" />
      <MoveStepButton updatePage={updatePage} direction="next" />
    </div>
  );
}

function MoveStepButton({ updatePage, direction }) {
  return direction === "prev" ? (
    <button onClick={() => updatePage((curr) => (curr === 0 ? 5 : curr - 1))}>
      PREV
    </button>
  ) : (
    <button onClick={() => updatePage((curr) => (curr === 5 ? 0 : curr + 1))}>
      NEXT
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

Done

As always, more CSS + Polishing. Full code can be found at this repo:

GitHub logo tlylt / thinking-in-react

View it at https://tlylt.github.io/thinking-in-react/

Thank you for reading and have a nice day.

Top comments (4)

Collapse
 
blnkspace profile image
AVI

Hey given this is for beginners, please add a comment about your useEffect that selecting DOM elements and mutating their styles is totally against React; and that no one in production should ever need to directly get a DOM element if they're using React the way it's meant to be used.

Collapse
 
tlylt profile image
Liu Yongliang • Edited

Hey Aviral, appreciate your candid reply.

I agree that my solution is not best practice. I guess when you say "using React the way it is meant to be used", it means this, straight from the React Doc:

React elements are immutable. Once you create an element, you can’t change its children or attributes. An element is like a single frame in a movie: it represents the UI at a certain point in time.

Hence, I have removed the useEffect that tries to manipulate DOM in a direct way. Instead of that, I will simply put the logic of toggling of the className="active" into the functional component that creates the span element. Would you say that is an improvement in terms of code quality?

From

function StepNumberBar({ total }) {
  return (
    <div className="stepNumberBar">
      {Array(total)
        .fill(null)
        .map((value, index) => (
          <span id={index} key={index}>
            {index}
          </span>
        ))}
    </div>
  );
}

To

function StepNumberBar({ total, current }) {
  return (
    <div className="stepNumberBar">
      {Array(total)
        .fill(null)
        .map((value, index) => (
          <span
            id={index}
            key={index}
            className={current === index ? "active" : "inactive"}
          >
            {index}
          </span>
        ))}
    </div>
  );
}

And the useEffect has been commented out

function ReferenceTable({ detail }) {
  const [currPage, updatePage] = useState(0);
  // useEffect(() => {
  //   var currP = document.querySelector("span[id='" + currPage + "']");
  //   var allSpan = document.querySelectorAll("span");
  //   allSpan.forEach((item) => {
  //     item.style.color = "black";
  //     item.className = item.className.replace(/active/g, "");
  //   });
  //   currP.style.color = "grey";
  //   currP.classList += ["active"];
  // }, [currPage]);
  return (
    <>
      <StepNumberBar total={detail.length} current={currPage} />
      <Explanation
        heading={detail[currPage].heading}
        content={detail[currPage].content}
      />
      <NavigationRow updatePage={updatePage} />
    </>
  );
}

I will amend the code in the article accordingly. Your further critique is appreciated!
Cheers,
Yong

Collapse
 
iamashusahoo profile image
Ashutosh

It was very informative but i lost you... maybe because m new to react and frontend... what do you suggest??

Collapse
 
tlylt profile image
Liu Yongliang

Hi Ashutosh, this article is meant to be a review and kind of a working example of the concepts explained in the original “thinking in React” article. Maybe I should make it clear next time that it is not exactly a follow-me-code kind of tutorial, but an invitation for you to read the original article which I linked to at the start, and to think about the development process in building React applications.

Cheers