DEV Community

JordanTaylorJ
JordanTaylorJ

Posted on

React BoardBuilder

This idea was sparked by question based forms that sort the user into a group (Find out which Harry Potter house you belong to). I had originally planned to make a site that leads you to the perfect bike for your style of riding. I quickly realized that every question would be dependent on the previous selection and formatting the data could quickly become cluttered, so I went back to the drawing board. I still liked the idea of a question form connected to a specific outcome.
The application I made is a similar idea, but applied to skateboards. This application works with you to build a complete skateboard based on what style of riding you want.

ReadMe.md

Features:

  • Homepage display with navigation panel
  • Step-by-step guide to build out your board
  • List created boards
  • Delete for each board

Tech Stack:

JavaScript React, MUI

Component Hierarchy:

Component Hierarchy

Organizing Data & useState

Working with nested data proved a difficult task. I reformatted the db.json several times throughout this build to keep the code clean and minimal. I also very quickly learned the value of good event and variable names in order to better manage props.

The useState hook updates React's internal state. boards state was kept inside of the App component in order for the children components to have access via props. State for newBoard was kept inside the BoardBuilder component along with the data for each step and MUI's activeStep functionality.

Controlled Component

Using state to derive input value makes a controlled input. In React, rather than using a selected attribute on <select>, value={state} should be used for controllable props.

Below you can see the complications in making <select> a controlled input. I wasn't able to pass the part variable as a value attribute because it was outside of the scope.

Selector component uncontrolled

I considered reformatting my db.json again, but I wanted to avoid adding a third fetch to my application. I tried to reformat to a checkbox instead, but that became an issue when I needed to restrict the selection to a single item. Eventually, I revisited the select tag with a simpler version (without using MUI).

I created a part variable in state and set value={part}. When a part is selected, handleChangePart saves it in state, and then it is passed to the callback function handleChange.

Selector component controlled

From there, the handleChange inside of the BoardBuilder component is responsible for building the newBoard object from the parts. Note the splice to make a copy of the previous state and the object key being targeted with the name value as an attribute of select.

const handleChange = (e) => {
    setNewBoard({...newBoard, [e.target.name] : e.target.value})
  };
Enter fullscreen mode Exit fullscreen mode

I also set the initial state of a newBoard to the first available option so that it can't default to an empty string.

const [newBoard, setNewBoard] = useState({
    deck: 'standard',
    trucks: 'standard kingpin',
    wheels: 'skatepark',
    risers: 'risers',
    griptape: 'griptape'
});
Enter fullscreen mode Exit fullscreen mode

Restful API

I use json-server for POST and DELETE requests. Like the example below, we must get the promise back from the fetch request before setting state. I used splice to make a copy, rather than modifying the original state of boards.

  const handleAddBoard = (newBoard) => {
    fetch("http://localhost:3001/completeboards", {
      method: 'POST',
      headers: { 
        "Content-Type": "application/json", 
      },
      body: JSON.stringify(newBoard),
    })
    .then(r => r.json())
    .then((data) => setBoards([...boards, data]))
  };
Enter fullscreen mode Exit fullscreen mode

useEffect & Dependency Arrays

I implemented the useEffect hook twice in this application. First, to fetch boards and initially setBoards and again inside of the BoardBuilder component to fetch and setStep.

I had initially thought that boards should be the dependency array value so that when that value changes, the side effect fires. However, I realized this is not necessary because boards is saved in state and state is updated after each POST or DELETE to the boards data. If a an object is inside the dependency array, it will infinitely fetch. I used empty dependency arrays on both so that they each only run once when the component initially renders.

  useEffect(() => {
    fetch("http://localhost:3001/completeboards")
    .then(r => r.json())
    .then((data) => setBoards(data))
  }, []);
Enter fullscreen mode Exit fullscreen mode

Note that the db.json should be run with --p 3001.

MUI

I worked with MUI for a consistent styling. The 'Stepper' component was used (as BoardBuilder) to map through the process of building a board. Within that, I needed to also map through the parts data inside of a Select component. I was able to clean up some of the BoardBuilder code from the original MUI formatting by adding steps into db.json and fetching it.

I also used MUI's AppBar for the navigation bar and their Card component to display the results.

Pros: The styling is consistent and clean throughout the application. It's easy to integrate and has good documentation (depending on the version, I used v5.8.4).

Cons: Similarly to working with MaterializeCSS, I had issues adding my own styling along with MUI. There is a learning curve for adding images and changing MUI's "theme". It also clutters your code quite a bit. Theres a lot of extra imports and even some items added to state.

Imports for changing theme:

import {ThemeProvider, createTheme } from '@mui/material/styles';

React Takeaways

  • React uses declarative code - JSX tells what to do, but not how to do it. AKA - no more document.createElement() and appending everything. 🙌
  • React components allow for reusable code! 👏 I only needed one BoardBuilder and one Result component and was able to map through with the data I wanted.
  • Setting state with a new object will cause a re-render with the updated info! No need to worry about complex DOM manipulation, again, less typing = efficiency. 👌

The overall key to React is understanding inverse data flow.

Checkout my GitHub repo to see the full code!

Top comments (0)