前言

React Hooks 已经在项目用使用了很久了, 写起来相当的 丝滑, useEffect 让我们印象深刻 , 始终围绕着两个字
那就是

没有 this 的世界非常美好

这句话似乎在 javascript 的世界里,没啥毛病, 严格意义上来说, js 没有指针的概念, this 也可以看做一个引用
我们熟悉的 useXX 的第二个 [dependency] 就是在对值的变化做比较

要实现一个 class 的 getter

1
2
3
4
const { name } = props
const transformedName = useMemo(() => {
return `我的名字是${name}`
}, [name])

这里的 memomemory 的简写

宿主在不停的调用 当前函数 ,当 name 更新 时 transformedName 会被重新执行

props 改变 更新 state 的场景也是异常的方便, 没有 this, 一切都是同步的

1
2
3
4
5
6
const { name: propsName } = props
const [name, setName] = useState<string>('')

useEffect(() => {
setName(propsName)
}, [propsName])

这时 useEffect 要执行另外一个函数

1
2
3
4
5
6
7
8
9
10
11
const { name: propsName } = props
const [name, setName] = useState<string>('')

+ const sayHello = () => {
+ console.log(`你是:${name} 吗?`)
+ }

useEffect(() => {
setName(propsName)
+ sayHello()
}, [propsName])

由于 sayHello 要间接的访问 name 所以我们应该 使用 useCallback

sayHello 包装一层 再添加为 useEffect 的依赖项

1
2
3
4
5
6
7
8
9
10
11
const { name: propsName } = props
const [name, setName] = useState<string>('')

+ const sayHello = useCallback(() => {
console.log(`你是:${name} 吗?`)
+ },[name])

useEffect(() => {
setName(propsName)
+ sayHello()
+ }, [propsName, sayHello])

这样可以保证其是一个 正常的数据流

不易察觉的引用

还是刚才的场景, 只不过我们的 setName 变成了 fetchApi 一个网络请求,让你看起来成本更高一点

很简单的代码, 当 propsids 更新时 我们就重新请求 api, 一切都是那么的完美,结果
你刷新页面打开 Network 面板, 头皮发麻

怎么死循环了 一直在请求数据 ?

1
2
3
4
5
6
7
const App = () => {
const { ids = [] } = props

useEffect(() => {
fetchApiByIds(ids)
}, [ids])
}

原因就是 const { ids = [] } = props 这里的 ids 默认值是 []idsundefined 他变成了 []

由于 [] 是一个新数组, 是一个新引用, 每次函数执行时 都是一个 [] 新引用, useEffect 内部使用 Object.is 作比较, 两者不相等 就一直执行了

1
2
3
4
5
6
const a = []
const b = []
const c = a

Object.is(a, b) // true
Object.is(a, c) // false

比较简单的解决办法就是

1
2
3
4
5
6
7
8
const defaultIds = []
const App = () => {
const { ids = defaultIds } = props

useEffect(() => {
fetchApiByIds(ids)
}, [ids])
}

或者

1
2
3
4
5
6
7
8
9
10
11
const App = () => {
const { ids } = props

useEffect(() => {
fetchApiByIds(ids)
}, [ids])
}

App.defaultProps = {
ids: []
}