DEV Community

Ourai L.
Ourai L.

Posted on

Front-end Shovel đź’© Diary #3: Refactoring the "Personal Profile"

Hey, Folks! From this note onwards, we’re diving into the actual code refactoring exercises!

The first piece of code that needs refactoring is in the user’s personal profile page, which I encountered when I took over the official website project.

In this system, users are divided into two types: individuals and project sponsors. Although their personal profiles look similar, there are differences in visual details and data structures. The layout structure of the profile page is roughly as shown in the figure below:

Profile Page Layout

The original code for the personal profile page was somewhat modular in other parts, but the most important sections, such as courses and activities, were all crammed into a single React component. It looked like this:

Code with Hidden

The code was messy and disorganized, making it difficult to discern the purpose of each section. Therefore, the refactoring approach was clear—split and encapsulate.

Based on the display of the personal profile page and the overall functionality of the website, I first divided the domain into four modules:

  • bounty for bounty tasks;
  • challenge for online and offline activities;
  • course for online courses;
  • quiz for quizzes.

I then encapsulated the code for each list rendering in the personal profile page into list view components and placed them in the corresponding modules. The data requests were also encapsulated into asynchronous functions and placed in the repository.js of each module:

Refactored Directory Structure

Since the interaction logic for each list was consistent—clicking “More” to load the next page—I further encapsulated a generic LoadableList component and placed it in the shared/components folder. This made the list view code much cleaner and more concise:

import LoadableList from '@/components/loadable-list'

import { fetchEnrolledCourseList } from '../../repository'
import EnrolledCourseItem from './EnrolledCourseItem'

function EnrolledCourseList({ list }) {
  return (
    <div>
      {list.map(({ series }, idx) => (
        <EnrolledCourseItem key={`course-${idx}`} data={series} />
      ))}
    </div>
  )
}

function EnrolledCourseListView({ params }) {
  return (
    <LoadableList
      params={params}
      fetch={fetchEnrolledCourseList}
      resolveResponse={res => ({ list: res.data.list, total: res.data.count })}
      renderList={list => <EnrolledCourseList list={list} />}
    />
  )
}

export default EnrolledCourseListView
Enter fullscreen mode Exit fullscreen mode

Next, I encapsulated the logic for switching between list views into ActivityTabList and declaratively passed the array of list views to it. The refactored personal profile page code looks like this:

import EnrolledCourseListView from '../../../course/views/enrolled-course-list'
import EnrolledChallengeListView from '../../../challenge/views/enrolled-challenge-list'
import AppliedBountyListView from '../../../bounty/views/applied-bounty-list'
import AnsweredQuizListView from '../../../quiz/views/answered-quiz-list'

import ActivityTabList from '../../widgets/activity-tab-list'

// List view declarations
const tabs = [
  { text: 'Course', view: EnrolledCourseListView },
  { text: 'Challenge', view: EnrolledChallengeListView },
  { text: 'Bounty', view: AppliedBountyListView },
  { text: 'Quiz', view: AnsweredQuizListView },
]

function IndividualProfileView({ user }) {
  return (
    <div className="pl-[410px] pb-14 pr-14">
      <ActivityTabList userId={user.user_id} tabs={tabs} />
    </div>
  )
}

export default IndividualProfileView
Enter fullscreen mode Exit fullscreen mode

The relative paths are used because a new profile module was added under domain to handle personal profile-related functions, and files within the same domain are constrained to use relative paths.

Finally, the original code in the app directory was changed to:

'use client'

export { default } from '#/domain/profile/views/individual-profile'
Enter fullscreen mode Exit fullscreen mode

In this way, the core code related to the personal profile was successfully moved from app to domain, completing the first code refactoring exercise!

Top comments (0)