DEV Community

Cover image for The Chronicles of a Rookie Developer: My 'React'ion Blog
Joohyun "Steve" Kim
Joohyun "Steve" Kim

Posted on

The Chronicles of a Rookie Developer: My 'React'ion Blog

It's finally here. The long awaited 2nd edition of The Chronicles of a Rookie Developer. (And yes, I just shamelessly upsold my self-made blog series. 😏)

Today's piece is about none other than the most popular front-end library out there, ReactJS. My one-liner opinion? Let's just say I can understand where its popularity stems from. But my affection won't make much of a blog post, so let's just dive right in, shall we?

As with the JS post, I thought I'd start by simply sharing my first impressions, some things I've learned along the way, then maybe leave you at the end with a few thoughts going forward.

One. Hooked on hooks.

I'm just going to come right out and say it. What the hook would we do without hooks? Okay that was corny. But seriously, everything is so much easier with hooks. I felt so cheated when I first encountered hook examples after spending a lot of time learning about functional vs. class components, using lifecycle methods, and this. Granted, not all of these things completely "go away" with hooks, and I don't regret covering all the bases because 1) I think it makes me a stronger developer and 2) I'm told there are still plenty of professional environments that use legacy code while the industry tries to catch-up. So for anyone who's just starting out on this path as I am, I still recommend you take the time to understand the old and the new. Also, I imagine that there's still a niche for class components since you could very well run into situations where it's just more sensible to use traditional methods -- whether it's to follow certain design patterns or something more subtle. (And I encourage you to comment below if you have any good examples.) But the point I want to make is that hooks are... well... they're wonderful.

Two. When unsure, useState.

One thing I noticed whenever I wanted to add a new feature or improve upon an existing one, is that I'm constantly setting and using state. Even with Redux in my application, I quickly realized that the need for local state is almost completely separate of what Redux is designed to solve. Redux deals with maintaining a single source of truth and helps prevent prop drilling, but it's more closely tied to the actual data you might need throughout your application as opposed to other non-critical or UI state. For instance, when I'm dealing with how my application behaves or how it should render a component, it usually means I just need to keep track of values that represent a certain state of my application.

Take this example. I was building out a quiz-like feature on my application and needed to incorporate a stepper to visibly show the user which question they were on because how annoying would a quiz or survey be if you had no idea how many more times you can expect to press "Next" before you're finished. So I've got a "parent" component that renders a separate component for each question in the quiz. (FYI, I am also using React Router to take the user to a new route for each of these renders.)

import { Routes, Route } from 'react-router-dom';
import QuestionOne from './quiz/QuestionOne';
import QuestionTwo from './quiz/QuestionTwo';
...

function Quiz() {
   return (
      <div>
         <Routes>
            <Route path="1" element={<QuestionOne />} />
            <Route path="2" element={<QuestionTwo />} />
            ...
         </Routes>
      </div>
   );
}
Enter fullscreen mode Exit fullscreen mode

Here's the part you stop to think what information is changing and therefore should belong in state (the current question in the quiz) and which component stays mounted and which ones come and go (the Quiz component is the parent that stays mounted while the Question components render on and off the page depending on the route). So then where should my stepper live and which component should keep track of which step the user is on? The answer is obviously the parent component, in this case, Quiz. Great, so we add that in.

...

function Quiz() {
   return (
      <div>
         <Routes>
            <Route path="1" element={<QuestionOne />} />
            <Route path="2" element={<QuestionTwo />} />
            ...
         </Routes>
         <Stepper steps={10}
            nextButton={<button onClick={handleNext}> Next </button>}
            backButton={<button onClick={handleBack}> Back </button>} />
      </div>
   );
}
Enter fullscreen mode Exit fullscreen mode

(For the actual Stepper component, I used Material UI but have simplified its properties for the purpose of this example.)

The props that are being passed are pretty self-explanatory but steps obviously refers to the total number of steps we want our stepper to have, whereas nextButton and backButton represent our two buttons that each take an onClick event that will handle navigating forward and back through our quiz. So now, how can we get the stepper to actually display which step we're currently on? Enter the useState hook.

//Quiz.js
...
import { useState } from 'react';

function Quiz() {
   const [activeStep, setActiveStep] = useState(0);
   return (
      <div>
         <Routes>
            <Route path="1" element={<QuestionOne setActiveStep={setActiveStep} />} />
            <Route path="2" element={<QuestionTwo setActiveStep={setActiveStep} />} />
            ...
         </Routes>
         <Stepper steps={10}
            activeStep={activeStep}
            nextButton={<button onClick={handleNext}> Next </button>}
            backButton={<button onClick={handleBack} disabled={activeStep === 0}> Back </button>} />
      </div>
   );
}
Enter fullscreen mode Exit fullscreen mode
//QuestionOne.js
import { useEffect } from 'react';

function QuestionOne({setActiveStep}) {
   useEffect(() => {
      setActiveStep(0);
   }, []);

   return ...
}

export default QuestionOne;
Enter fullscreen mode Exit fullscreen mode
//QuestionTwo.js
...

function QuestionTwo({setActiveStep}) {
   useEffect(() => {
      setActiveStep(1);
   }, []);

   return ...
}

export default QuestionTwo;
Enter fullscreen mode Exit fullscreen mode

Couple of things are happening here but first I set up some local state in my Quiz component with an initial value of 0 and name it activeStep. I then pass this as a prop to my Stepper component in order to tell it what step we're on (and also use it to disable the "Back" button when we're on the first step/question). Then I pass the setActiveStep method to the children components so that they may use it in their useEffect which I have set up to basically mimic the componentDidMount lifecycle.

So now, whenever the user renders a Question component, the parent will know which one the user is looking at since it holds the activeStep state and shares the corresponding value to the Stepper as well.

Some other great examples of places I utilized local state are with my Login/Signup drawers, controlled forms, various snackbars, dialogs, toggles and many more. When building with React, you will never be short of places to use and manage state since literally any value that needs to change will mean that it needs to be stored in either local or global state.

But it's important to bear in mind that there is no right answer when it comes to which of the two you should use to store your information. Even the Redux documentation will tell you that it's often a choice developers have to make and that it's important to find a good balance. Some developers are more comfortable with storing absolutely everything in a global state management tool (like Redux) whereas I personally find it an anti-pattern to include every bit of information in the global state when we can utilize local state.

So next time you're wanting to develop a feature but feel stuck or clueless on where to begin, try setting up some values in state and focus on what and how those values need to change in order to implement your desired behavior.

Three. Keep a mental note of the component tree.

Lastly, I would like to end with this. I learned that taking sufficient time to plan and having a solid grasp of how you will build out your component tree before you get started on a project can help tremendously with your application control flow. Once a user starts freely navigating around in your app, it quickly becomes apparent that you need to be somewhat conscious of which components are constantly mounting and unmounting. This is particularly because the lifecycle of a component is crucial in determining where certain state values should reside and if you're not making a mental note of it as you code, it can lead to some very unexpected behavior down the road (especially as your application grows bigger in both size and functionality).

As always, thank you for taking the time to read and, once again, stay tuned for the next edition of The Chronicles of a Rookie Developer!

Top comments (0)