capstone debugging: learnings
Tags: beginners, learning, bootcamp, fullstack
Published: 2024-06-07 21:34:46

The clock on my monitor silently ticked, while I cried in JSX fragments and Spring beans. It was Week 15 and 16- the final stretch of my coding bootcamp and we were tasked with creating a full-stack app. Time was tight from having to research and execute new concepts, and the bugs- oh someone call pest control! 😅

![Cartoon of Emotional Gal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ww86p57740jx0jsku28t.jpg)

Though it was tough, I did end up finishing and learned a bunch in the process. My project was a volunteer-driven maps application that allows users to crowdsource accessibility information as well as explore and search accessible places near them. 

While my Github [readme](https://github.com/ashleyd480/access-map-app-capstone) shares more in-depth details of my learnings, I wanted to also share a behind the scenes view of 4 bugs I faced and how I debugged them:

## Table of Contents

1. [Duplicate Data Seeding](#1-duplicate-data-seeding)

2. [Rating Button Not Showing as Checked](#2-rating-button-not-showing-as-checked)

3. [Uncontrolled Component Warning](#3-uncontrolled-component-warning)

4. [Conditional API Calls](#4-conditional-api-calls)

---

## 1. Duplicate Data Seeding
**Issue:** When seeding data without using a list, duplicate entries were created.
**Solution:** Created a variable to store the data seeder return and referenced this variable in other functions. This prevented duplication when checked in Postman.

To ensure the app had places loaded along with user reviews (to simulate a database of Maps places) and feature tags, a data seeding mechanism was used. 🌱. My approach at the beginning was to call the return of the entity seeder class. For example, in my `reviewSeeder`, I called `userSeeder.seedUsers()`, thinking that would just get me the return value of the initial list of 10 seeded users. Instead, I ended up with duplicates—Postman showed 20 users instead of 10 😬. When checking that list, I noticed that the usernames repeated twice, i.e. `user1` appeared twice with `id` of 1 and 11. 

After an hours-long trip down a rabbit hole, I realized that `userSeeder.seedUsers()` appeared to invoke the seeder function again instead of just returning the initial seeded list. 
To fix this, I created a variable to hold the data seeder's return value and referenced this variable in subsequent functions. This change effectively prevented duplication, confirmed by rechecking in Postman. 

```
     public void run(String... args) throws Exception {
            List<User> seededUsers= userSeeder.seedUsers();
            // Seed users first
            List<FeatureTag> seededTags = tagSeeder.seedTags(); // Then, seed tags first
            List<Place> seededPlaces = placeSeeder.seedPlaces(seededTags); // Pass seeded tags to places
            reviewSeeder.seedReviews(seededPlaces, seededUsers); // Pass seeded places and tags to reviews (because reviews can only exist with a place and places have tags)
        }
```


## 2. Rating Button Not Showing as Checked
**Issue: **The rating button wasn’t properly working or showing as checked upon user selection. 
**Solution:** Used the checked attribute to control the selected radio button based on the component's state, as well as corrected mapping logic.

When adding a review, users are also prompted to rate the accessibility of the place from 1-5. Getting the rating buttons to display as checked was another challenge ⭐. 

Kudos to my instructor who worked through with me during office hours.
Firstly, we fixed the mapping logic. The way rating radio buttons are generated is to take the array of ratings [1, 2, 3,4,5] and then map through them.

```
{[1, 2, 3, 4, 5].map((value) => { ... })}
```
A `map` typically takes 2 parameters: the value (current element of the array being processed and the index of the current element in the array. In this case, `(value) => { ... }` is an anonymous function that takes value as its parameter. and it is saying for each rating number, we want to have it be a radio button.

```
{[1, 2, 3, 4, 5].map((value) => {
     
    return ( // return a radio button for each number in the array 
       <Form.Check
             key={value}                  
 type="radio"
             label={value} // set label text for radio button 
             name="rating"
             value={value} // set value attribute to the current value we are mapping over 
             checked={formData.rating === value.toString()}
             onChange={handleChange}
           />
)
```

After that, I was able to confirm that yes, the value of the user’s selection was read by using a `console.log` and confirming the value was read on click. However, the button still appeared as unchecked, and that can be confusing to the end user.

We researched and learned that the `checked` attribute is what helps  React determine which button to select when the form renders. This meant that there was an issue with how we were defining the statement in `checked.` The values in the bracket had to evaluate to true and we were comparing `formData.rating` to `value` (which was the value of the radio button generated from our mapping). 

We confirmed that this comparison had to evaluate to true as we wrote `checked = {false}`; the formData.rating value was read on the console, but the button was not checked - which proves that when the comparison is `false`, a check will not appear visually in our UI. 

Therefore, we dug a bit further into how we were getting those values and comparing them.

formData.rating is set using the `handleChange` which sets the rating value when the user clicks on a radio button. (Essentially, the function  looks at `event.target.name` aka fields that triggered the change, and gets its value and sets it to form data.

```
const handleChange = (event) => {
    const { name, value } = event.target; // destructures the event.target with the keys in brackets 
// this way, we can use `name` and `value` variables vs `event.target.name, event.target.value
    setFormData((prevFormData) => ({
        ...prevFormData, //takes the form data and makes copy of it 
        [name]: value, //gets value for fields that triggered the change and sets it to form data.
    }));
};
```

We ran a `console.log` to compare `formData.rating` and `value.` In the end, we saw the issue was a type mismatch. After researching and seeing a suggested `toString` method online, we used that with our own code. `formData.rating === value.toString()` generated `true` and the check was now appearing on the UI. ✅

We could also verify this with the `console.log`. You can see when the user clicks 2. 
Line 88 is `formData.rating` and Line 89 is 	`value.toString()`. You can see 5 lines appear - which is from our mapping of the 5 ratings, and for each it checks to see if the `value` we are mapping over from the array is equal to the user’s selection. When it is mapped over 2, that matches what the user selected, so the check appears visually in the UI. 


![Rating Console Log](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5f40r2orcks30qagqauw.png)


## 3. Uncontrolled Component Warning
**Issue:** Input fields were locked because they were directly bound to `userData`.
**Solution:** Made a copy of `userData` to allow edits and saved changes on submit. This prevented the form from locking while enabling updates.

When designing the `Edit Account` page, I wanted data from `My Account` (which was retrieved from a `GET` mapping call to also populate on `Edit Account`).  I used the `UserData` context provider in React to carry over those values. While the information did port over correctly to `Edit Account`, the input fields were locked 🔒, preventing edits. 

Shoutout to my mentor who helped me to battle this bug. The console showed an error of “uncontrolled component warning.” We learned this error is when the state of a component is not being controlled by React itself, aka React doesn’t have complete control over the `Edit Account` form’s input fields. Yes, React is a control freak. 😉

Fields were directly bound to `userData` (which was set from that aforementioned API call on `My Account`). This resulted in the fields being "locked" and preventing any edits. This also means that when I was  trying to edit the input fields, I was essentially trying to edit the original userData. React doesn't allow direct changes to props because they are supposed to be immutable. So, trying to edit the input fields directly would essentially be trying to modify immutable data, which React won't allow.

Also, when an input field is directly bound to a piece of data- in this case `userData`, React cannot fully control the state of that input field. 

We resolved it by creating a copy of `userData`, allowing modifications without altering the original until submission.

```
  const [formData, setFormData] = useState({ ...userData });
```
`formData` is a variable that refers to that copy of `userData` (with its key-value pairs of data details), so in our form fields, we can use the dot notation of `value={formData.email}`. 

 This also fixed the uncontrolled component warning, ensuring form fields were populated with the initial `userData` values but remained editable. Upon submitting, changes were saved back to the original user data with a `PUT` request,  ensuring a smooth and functional user experience.

Finally, the user is redirected back to `My Account` after a successful `PUT` call, and that is where `GET` mapping happens to retrieve the user info and set it to the `userData` context provider- ensuring both the backend and the frontend context providers’ values are updated 💾. 


## 4. Conditional API Calls 
**Issue:** Fetch API calls wouldn’t populate with data on the frontend 
**Solution:** Ensured API call was made only if the `username` was truthy, triggering the call once the username was available.

A peculiar issue with populating data from fetch API arose 🚧. The first time I noticed this was when trying to get `My Account` details to populate by username. My API call required the `username` value.

```
 const responseData = await fetchData(`users?username=${username}`);
```

I started with using local storage to store the username upon a successful sign in, but the API call to get account details would not return anything. I even did a `console.log` to ensure the username was being correctly read. The instructor gave a hint on how local storage can be slow to load. Thus, I then tested using a `username` context provider to pass the value - thinking this would resolve it. Still, there was no luck in rendering the API call’s return.

To prove that it was not an issue with the system reading the `username` value, I even tried to hardcode a `username` value of const username = `user1` before the API call, and that worked. Something else was brewing. 

As I initially had an `address` entity linking to `user`, I tried to change the dot notation format to `users.address`, `users.` , `address`, etc- all to no avail either. I then thought perhaps the `address` entity was giving me issues due to how it was set up on Spring Boot with the one-to-one cascade, so I commented it out to see if I could at least get the `user` information to populate. It did! 
When I uncommented out `address`, then the `address` would populate. I tested this a few times with mixed results, and noticed I had to wait a bit to uncomment out `address` for both the user and address to display. This gave me another hint, that perhaps we had to wait for the user information to populate. 

A few hours later, what I learned is that given that API calls are asynchronous, there was a possibility that the data might not be available at the time of the call. Also, local storage and context providers are asynchronous too. That means JavaScript won't wait for the local storage or context operation to finish before continuing to execute the API calls. 

That  means that when we attempted to fetch "My Account" details based on the `username`, there was no guarantee that the username would be available immediately. It could take some time for the local storage to be accessed and the username to be retrieved. 


By implementing a conditional API call that triggered only if the username was truthy, I ensured the address field was populated correctly. This method checked if the username was available before making the API call, allowing the address data to load appropriately. This method highlighted the importance of conditional logic in ensuring seamless data fetching and rendering in the UI 🎉

```
useEffect(() => {
  if (username) {
    fetchUserData(username);
  }
}, [username]);
```