学习React哲学的一次实践

879 阅读3分钟

不完全和原文步骤一致。

结果展示:

需求描述

一个展示颜色的小应用。

  • 共8种色系,红橙黄绿蓝青紫灰。每种色系下有更详细的颜色展示。
  • 点击卡片切换色系,展示颜色列表,点击某个颜色可改变卡片的颜色。
  • 鼠标悬浮展示颜色的名称和十六进制值,点击可复制值到剪贴板。

数据

[
    {"set": "red", "list": [{ "name": "绾", "value": "#A98175" }……]},
    {"set": "orange", "list": [{ "name": "褐色", "value": "#6E511E" }……]},
    ……
]

第一步:从UI层面划分组件层级

  1. App(红色):整个应用
  2. CircleGroup(橙色):当前色系对应的颜色列表
  3. Title(蓝色):标题
  4. CardGroup(绿色):8种色系卡片
  5. Circle(粉色):颜色单元
  6. Card(黑色):色系卡片单元
  7. Char(黄色):字母单元

组件层级

  • App
    • CircleGroup
      • Circle
    • Title
      • Char
    • CardGroup
      • Card

第二步:分析组件功能并提炼数据

从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>
)

交互优化