本文是基于React + Taro + Typescript + SCSS实现的一个TODOLIST DEMO,作为本次青训营的实践小项目。该项目简单运用了React Hooks和css flex布局、animation等。具体实现的功能如下:
- 表单提交编辑代办,内容校验
- 计时器创建/销毁
- 增/删代办事项,以及对应动画
Hooks应用
本次项目使用的是函数式组件,每个组件声明了Interface用于更直观的了解组件的功能,其基本结构如下:
import React from 'react'
import Taro from '@tarojs/taro'
import style from './main.module.scss'
namespace ToDoItem {
export interface Props {
task: ObjectType
styleVanishName?: string
onDel(taskId: number): void
onUpdate(task: ObjectType, taskStatus: string): void
}
}
const ToDoItem: React.FunctionComponent<ToDoItem.Props> = React.memo(props => {
// ...
return (
<>
<View className={style.item}>子项<View>
</>
)
})
export default ToDoItem
a. 编辑表单的创建
核心思想: 利用React.useRef(),它用于获取持久化引用,可以访问子组件的属性或方法,常用于在函数组件中存储和访问 DOM 元素或其他需要在多次渲染之间保持一致的值。
// 编辑倒计时时长:
const clockLengthRef = useRef<any>()
const clockLen = clockLengthRef.current.value || 0
<Input
type='number'
placeholder='> 0'
className={style.inputSmall}
placeholderClass={style.placeholder}
onBlur={handleCheck}
ref={clockLengthRef}
disabled={!useClock}
/>
b. 计时器的实现
核心思想: 利用React.useEffect() + React.useRef()。
const timerID = React.useRef<any>()
// 倒计时计算
React.useEffect(() => {
clearInterval(timerID.current)
setCountDownTime(task.clockLength * 60 * 1000)
if (startClock){
timerID.current = setInterval(() => {
setCountDownTime(pre => pre - 1000)
}, 1000)
}
}, [startClock, task])
c. 优化:减少不必要的渲染
核心思想: 利用React.useCallback()/React.useMemo() + React.memo()。避免父组件状态的改变对子组件产生不必要的刷新。
不少优质博客有介绍这几个hooks的用法,这里简单总结一下它们的实用用法:
React.useCallback(()=>{},[deps])包裹传入子组件的方法React.useMemo(()=>{},[deps])包裹传入子组件的对象,如一个列表。React.memo()包裹子组件自己
这样就可以监听依赖项的变化刷新子组件,而不需要父组件刷新子组件一起跟着全刷新。实现了渲染上的优化。
原因:React.memo()检测的是props中数据的栈地址是否改变,父组件重新构建的时候,会重新构建父组件中的所有函数。useCallBack和useMemo都是将包裹的内容进行缓存,使得只在依赖项数组中的依赖项发生变化时才会重新创建新的函数实例,从而避免不必要的重新渲染。需要谨慎使用useCallBack和useMemo,一定记得需要配对React.memo()使用,缺了一个都可能导致性能不升反“降”,毕竟无意义的浅比较会消耗性能。
CSS 应用
删除代办主要做了两种动画:一种是删除最后一条,直接运用渐变消失;一种是删除非最后一条,即有后续队列的,需要实现删除元素计算后续队列,再执行上移。动画实现如下:
.animationVanish {
animation-name: vanish 0.6s ease-in;
}
@keyframes vanish {
0% {opacity: 1}
50% {opacity: 0.7}
75% {opacity: 0.3}
100% {opacity: 0}
}
后者需要在删除时找到后续队列数组,作为一个整体执行上移动画,具体实现如下:
.animationMove {
animation: movetop 0.6s ease-in;
}
@keyframes movetop {
0% {transform: translateY(0)}
100% {transform: translateY(-170px)}
}
未来可以继续的工作:
- 配合json-server
- 实现图片上传
- 实现发布后修改任务