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:
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:
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:
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
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
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'
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)