Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for capstone debugging: learnings
Ashley D
Ashley DSubscriber

Posted on • Edited on

     

capstone debugging: learnings

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

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 Githubreadme 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

  2. Rating Button Not Showing as Checked

  3. Uncontrolled Component Warning

  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 myreviewSeeder, I calleduserSeeder.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 withid of 1 and 11.

After an hours-long trip down a rabbit hole, I realized thatuserSeeder.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)        }
Enter fullscreen modeExit fullscreen mode

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) => { ... })}
Enter fullscreen modeExit fullscreen mode

Amap 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}           />)
Enter fullscreen modeExit fullscreen mode

After that, I was able to confirm that yes, the value of the user’s selection was read by using aconsole.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 thechecked 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 inchecked. The values in the bracket had to evaluate to true and we were comparingformData.rating tovalue (which was the value of the radio button generated from our mapping).

We confirmed that this comparison had to evaluate to true as we wrotechecked = {false}; the formData.rating value was read on the console, but the button was not checked - which proves that when the comparison isfalse, 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 thehandleChange which sets the rating value when the user clicks on a radio button. (Essentially, the function looks atevent.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.    }));};
Enter fullscreen modeExit fullscreen mode

We ran aconsole.log to compareformData.rating andvalue. In the end, we saw the issue was a type mismatch. After researching and seeing a suggestedtoString method online, we used that with our own code.formData.rating === value.toString() generatedtrue and the check was now appearing on the UI. ✅

We could also verify this with theconsole.log. You can see when the user clicks 2.
Line 88 isformData.rating and Line 89 isvalue.toString(). You can see 5 lines appear - which is from our mapping of the 5 ratings, and for each it checks to see if thevalue 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

3. Uncontrolled Component Warning

Issue: Input fields were locked because they were directly bound touserData.
Solution: Made a copy ofuserData to allow edits and saved changes on submit. This prevented the form from locking while enabling updates.

When designing theEdit Account page, I wanted data fromMy Account (which was retrieved from aGET mapping call to also populate onEdit Account). I used theUserData context provider in React to carry over those values. While the information did port over correctly toEdit 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 theEdit Account form’s input fields. Yes, React is a control freak. 😉

Fields were directly bound touserData (which was set from that aforementioned API call onMy 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 caseuserData, React cannot fully control the state of that input field.

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

  const [formData, setFormData] = useState({ ...userData });
Enter fullscreen modeExit fullscreen mode

formData is a variable that refers to that copy ofuserData (with its key-value pairs of data details), so in our form fields, we can use the dot notation ofvalue={formData.email}.

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

Finally, the user is redirected back toMy Account after a successfulPUT call, and that is whereGET mapping happens to retrieve the user info and set it to theuserData 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 theusername 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 getMy Account details to populate by username. My API call required theusername value.

 const responseData = await fetchData(`users?username=${username}`);
Enter fullscreen modeExit fullscreen mode

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 aconsole.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 ausername 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 theusername value, I even tried to hardcode ausername value of const username =user1 before the API call, and that worked. Something else was brewing.

As I initially had anaddress entity linking touser, I tried to change the dot notation format tousers.address,users. ,address, etc- all to no avail either. I then thought perhaps theaddress 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 theuser information to populate. It did!
When I uncommented outaddress, then theaddress would populate. I tested this a few times with mixed results, and noticed I had to wait a bit to uncomment outaddress 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 theusername, 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]);
Enter fullscreen modeExit fullscreen mode

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

10+ year client support roles, and currently in technical apprentice program (Bootcamp started Feb 2024)
  • Joined

More fromAshley D

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp