背景
#
这个项目有点老旧
"dva": "2.1.0",
"react": "^16.2.0",
我打算用最新的 react 和 @reduxjs/toolkit 开始重构,旧的依赖只能使用 connect 方式来获取 store ,使用 useSelector 和 useDispatch 很方便也很易读。@reduxjs/toolkit
提供的immer和actionCreater也很好用。
新版本的 react-redux 才提供了 useSelector 和 useDispatch ,但是新版本会导致 dva 出错。无奈放弃了新版本,还是用旧版本的 connect 。还好这不影响对代码进行组件化。
首先重构的是一个643行的组件,它的render方法有580行。
重复代码
#
很容易的发现了一大段重复代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<div className={styles.numbers_wrapper}>
<span className={styles.number_item}>
<span className={styles.number}>{detail.properties.member_count}</span>
<span className={styles.text}>班级总人数</span>
</span>
<span className={styles.number_item}>
<span className={styles.number}>{detail.properties.male_count}</span>
<span className={styles.text}>男生人数</span>
</span>
<span className={styles.number_item}>
<span className={styles.number}>{detail.properties.female_count}</span>
<span className={styles.text}>女生人数</span>
</span>
<span className={styles.number_item}>
<span className={styles.number}>{detail.properties.group_count &&( detail.properties.group_count[userDetail.detail.id] || 0)}</span>
<span className={styles.text}>分组数</span>
</span>
<span className={styles.number_item}>
<span className={styles.number}>{(detail.properties.user_lecture_count ? (detail.properties.user_lecture_count[userDetail.detail.id] || 0) : 0)}</span>
<span className={styles.text}>授课次数</span>
</span>
</div>
|
可以将他们组织成2个组件
1
2
3
4
5
6
7
8
|
function NumberWithLabel({ label, count }) {
return (
<span className={styles.number_item}>
<span className={styles.number}>{count}</span>
<span className={styles.text}>{label}</span>
</span>
);
}
|
1
2
3
4
5
6
7
|
<div className={styles.numbers_wrapper}>
<NumberWithLabel label="班级总人数" count={member_count} />
<NumberWithLabel label="男生人数" count={male_count} />
<NumberWithLabel label="女生人数" count={female_count} />
<NumberWithLabel label="分组数" count={group_count && (group_count[userId] || 0)} />
<NumberWithLabel label="授课次数" count={(user_lecture_count ? (user_lecture_count[userId] || 0) : 0)} />
</div>
|
数据使用 connect 获取,这样代码看起来简单读了。
封装组件
#
然后发现了一大段代码可以封装成一个独立的组件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
<div className={styles.card_body}>
{
groupList && groupList.map((group, index) => (
<div className={styles.group_item} key={index} onDrop={() => handleStudentDrop(group)} onDragOver={handleStudentDragOver} >
<div className={styles.group_item_header}>
<span className={styles.group_item_info}>
{group.name}
<i className={styles.trophy_icon} />
</span>
<span className={styles.group_item_btns}>
</span>
<Radio className={styles.group_student_list_radio} checked={group.id === curGroup} onClick={() => selectCurGroup(group.id)} />
</div>
<div className={styles.group_student_list}>
{
group.members && group.members.length === 0 ? (
<div style={{
textAlign:'center',
fontSize:14,
color:'#666',
}}>该小组没有学生</div>) : (
group.members.map((stu, i) => (<StudentItem
isDrage
key={i}
stu={stu}
group={group}
gender={stu.gender}
headimg_res_url={stu.headimg_res_url}
name={stu.name}
groupStuStyle
onDragStart={() => handleDragStart(stu, group.id)}
onSetLeader={e => handleSetLeader(e, stu,group)}
onSetHeader={e => handleSetHeader(e, stu)}
/>))
)
}
</div>
</div>
))
}
</div>
|
还有它相关的方法都是像下面👇这样的 dispatch ,大概有五六个。它们都写在 render 函数内,占用了很大的篇幅,对阅读代码有很大的影响。
1
2
3
4
5
6
7
8
9
10
11
12
|
const handleDragStart = (stu, org_id) => {
dispatch({
type: 'classDetail/updateState',
payload: {
onDragStudent: {
...stu,
org_id,
type:!!org_id,
},
},
});
};
|
我将这个 dispatch
中的 action
放到一个 action_creater.js
文件中,将组建放在 GroupItem.jsx
文件中。这可以让次要的信息不占用篇幅,主要的信息就会一目了然了。
这段代码就变成了2个组件和1个 action creater
文件。相关的代码封装在一起,再也不用为了看一个函数而滚动300行代码或者要用搜索才能找到了。
这3个文件目前都只有40-50行。
原来643行的文件,现在是490行,优化掉了150行(23%)的代码。使代码有了更好的封装,改善了阅读/维护难度。