不完全和原文步骤一致。
结果展示:

需求描述
一个展示颜色的小应用。
- 共8种色系,红橙黄绿蓝青紫灰。每种色系下有更详细的颜色展示。
- 点击卡片切换色系,展示颜色列表,点击某个颜色可改变卡片的颜色。
- 鼠标悬浮展示颜色的名称和十六进制值,点击可复制值到剪贴板。
数据
[
{"set": "red", "list": [{ "name": "绾", "value": "#A98175" }……]},
{"set": "orange", "list": [{ "name": "褐色", "value": "#6E511E" }……]},
……
]
第一步:从UI层面划分组件层级

- App(红色):整个应用
- CircleGroup(橙色):当前色系对应的颜色列表
- Title(蓝色):标题
- CardGroup(绿色):8种色系卡片
- Circle(粉色):颜色单元
- Card(黑色):色系卡片单元
- Char(黄色):字母单元
组件层级
- App
- CircleGroup
- Circle
- Title
- Char
- CardGroup
- Card
- CircleGroup
第二步:分析组件功能并提炼数据
从UI和交互两个方面来梳理一下主要组件的功能。
CircleGroup
-
UI:展示当前色系对应的颜色列表及每个颜色的信息。
数据:颜色列表 -
交互:改变卡片颜色
<CircleGroup
list={this.getColorList()}
onChange={this.toggleChangeCircle.bind(this)}
/>
Circle
- UI:展示单个颜色
数据:颜色十六进制值 - 交互:点击复制颜色,改变卡片颜色
<Circle
key={name}
name={name}
color={value}
index={index}
onClick={() => {
onChange(value)
}}
/>
列表渲染需要加key
CardGroup
- UI:展示8张色系卡片
数据:卡片颜色,当前索引值 - 交互:切换当前色系
<CardGroup
cardColor={cardColor}
currentIndex={currentIndex}
onChange={this.toggleChangeCard.bind(this)}
/>
Card
- UI:展示某个色系颜色
数据:颜色十六进制值 - 交互:点击
<Card
key={val}
index={index}
isActive={index === currentIndex}
bgcolor={cardColor[index]}
onClick={() => {
onChange(index)
}}
/>
第三步:确定state的最小表示
所有的数据:
- 每种色系对应的颜色列表及颜色相关信息
- 为实现切换效果,需要一个变量存储当前色系,在此存储了索引值
- 为实现改变卡片颜色,需要一个存储所有卡片颜色的数组
颜色列表可由当前色系推出,不是state,所以state就是:
- 当前色系索引值
- 卡片颜色数组
this.state = {
cardColor,
currentIndex: 0
}
第四步:state的位置
这个应用比较简单,所有App的子组件都需要依赖state渲染,所以state放在App中即可。
第五步:反向数据流
单向数据流自上而下的渲染出了应用,而state只能由拥有它们的组件进行更改,那么通过子组件的点击行为改变数据需要借助回调函数。
toggleChangeCircle(color) {
const { cardColor, currentIndex } = this.state
const arrTemp = cardColor
arrTemp[currentIndex] = color
this.setState({
cardColor: arrTemp
})
}
toggleChangeCard(index) {
this.setState({
currentIndex: index
})
}
具体调用见前面的代码
第六步:利用render props添加动画
借鉴vue的过渡组件概念创建了<Transiton>组件,在不同时刻给子组件添加和删除类名,实现方式借助render props。
<Transiton>组件中
return (
<React.Fragment>
{children(classnames(
enter ? null : 'before-enter',
enter && !leave ? 'enter' : null)
)}
</React.Fragment>
)
<Card>组件中使用
return (
<Transition num={index* 20}>
{str => (
<div
className={cls(
name,
'card',
str,
isActive ? "active" : null
)}
style={{ backgroundColor: bgcolor }}
onClick={onClick}
>
</div>
)}
</Transition>
)
交互优化
- motion
缓动函数速查表