本节代码对应 GitHub 分支: chapter6
# 封装 UI 代码
现在 Album 里面的 JSX 过于庞大,影响可读性,可以做一下封装。
将复杂渲染的代码拆解如下:
const renderTopDesc = () => {
return (
<TopDesc background={currentAlbum.coverImgUrl}>
<div className="background">
<div className="filter"></div>
</div>
<div className="img_wrapper">
<div className="decorate"></div>
<img src={currentAlbum.coverImgUrl} alt="" />
<div className="play_count">
<i className="iconfont play"></i>
<span className="count">{getCount (currentAlbum.subscribedCount)}</span>
</div>
</div>
<div className="desc_wrapper">
<div className="title">{currentAlbum.name}</div>
<div className="person">
<div className="avatar">
<img src={currentAlbum.creator.avatarUrl} alt="" />
</div>
<div className="name">{currentAlbum.creator.nickname}</div>
</div>
</div>
</TopDesc>
)
}
const renderMenu = () => {
return (
<Menu>
<div>
<i className="iconfont"></i>
评论
</div>
<div>
<i className="iconfont"></i>
点赞
</div>
<div>
<i className="iconfont"></i>
收藏
</div>
<div>
<i className="iconfont"></i>
更多
</div>
</Menu>
)
};
const renderSongList = () => {
return (
<SongList>
<div className="first_line">
<div className="play_all">
<i className="iconfont"></i>
<span > 播放全部 <span className="sum">(共 {currentAlbum.tracks.length} 首)</span></span>
</div>
<div className="add_list">
<i className="iconfont"></i>
<span > 收藏 ({getCount (currentAlbum.subscribedCount)})</span>
</div>
</div>
<SongItem>
{
currentAlbum.tracks.map ((item, index) => {
return (
<li key={index}>
<span className="index">{index + 1}</span>
<div className="info">
<span>{item.name}</span>
<span>
{getName (item.ar)} - {item.al.name}
</span>
</div>
</li>
)
})
}
</SongItem>
</SongList>
)
}
return (
<CSSTransition
in={showStatus}
timeout={300}
classNames="fly"
appear={true}
unmountOnExit
onExited={props.history.goBack}
>
<Container>
<Header ref={headerEl} title={title} handleClick={handleBack} isMarquee={isMarquee}></Header>
{!isEmptyObject (currentAlbum) ?
(
<Scroll
bounceTop={false}
onScroll={handleScroll}
>
<div>
{ renderTopDesc () }
{ renderMenu () }
{ renderSongList () }
</div>
</Scroll>
)
: null
}
{ enterLoading ? <Loading></Loading> : null}
</Container>
</CSSTransition>
)
这样整个返回的 JSX 代码就清爽了不少,给人一目了然的感觉。
# useCallback 优化 function props
将传给子组件的函数用 useCallback 包裹,这也是 useCallback 的常用场景。
const handleBack = useCallback (() => {
setShowStatus (false);
}, []);
const handleScroll = useCallback ((pos) => {
//xxx
}, [currentAlbum]);
以此为例,如果不用 useCallback 包裹,父组件每次执行时会生成不一样的 handleBack 和 handleScroll 函数引用,那么子组件每一次 memo 的结果都会不一样,导致不必要的重新渲染,也就浪费了 memo 的价值。
因此 useCallback 能够帮我们在依赖不变的情况保持一样的函数引用,最大程度地节约浏览器渲染性能。
OK,歌单详情模块现在开发基本完成。
阅读全文