计算属性,是Vue中一个非常重要也无比好用的特性,例如:
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
在模版中,即可直接使用reversedMessage
:
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
使用计算属性,一方面可以简化语法,也就说我们不需要每次都手动写一遍message.split('').reverse().join('')
了,或者是非常麻烦地将其封装成一个函数;另一方面,计算属性会自动被缓存,这样可以让应用的性能更好。
那么说了这么多,有没有办法在React中实现类似的逻辑呢?
当然!
得益于React 16.8新推出的Hooks特性,我们可以对逻辑进行更优雅的封装
首先,我们来介绍一下useMemo
:
掌握了useState
和useEffect
其实远远是不够的,useMemo
这样的hook看起来小众,但其实用处非常大!
官方对useMemo
的介绍在这里, 简而言之,就是我们传入一个回调函数和一个依赖列表,React会在依赖列表中的值变化时,调用这个回调函数,并将回调函数返回的结果进行缓存后,返回给上一层:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
对应到本文最开始的Vue例子,我们用useMemo
的实现就是:
function App (props) {
const [message, setMessage] = useState('hello')
const reversedMessage = useMemo(() => (
message.split('').reverse().join('')
), [message])
return (
<div>
{message}
{reversedMessage}
</div>
)
}
当message
发生变化时,React会重新计算message.split('').reverse().join('')
,然后把得到的结果进行缓存,并且返回给reversedMessage
变量。
是不是没有想象中那么难?
不过,上面的例子中还有一个小问题,那就是对异步逻辑的处理。
const replyMessage = useMemo(() => (
api.fetchReply(message) // 这里是一个异步的网络请求
), [message])
由于api.fetchReply
调用得到的返回值是Promise
,所以reversedMessage
的类型变成了Promise<string>
而非string
。
这时,我们就需要使用useAsyncMemo
了 。(是的,它比useMemo
就是多了个async)
由于useAsyncMemo
不是React提供的hook ,我们需要先通过npm
或yarn
安装它:
$ npm install use-async-memo --save
# 或者
$ yarn add use-async-memo
然后,我们来使用useAsyncMemo
改写上面的逻辑:
import {useAsyncMemo} from 'use-async-memo'
const replyMessage = useAsyncMemo(async () => ( // 这里的回调函数变成了async函数
await api.fetchReply(message) // 这里也变成了await
), [message])
对比一下就会发现,useAsyncMemo
的第一个参数不再是普通的函数了,而变成了一个异步函数,这里我们使用了es6的async function特性。除此之外,和之前useMemo
的写法非常相似。这样,我们就可以通过replyMessage
变量获取到异步函数返回的值了。
啥是async await?关于async函数的介绍,可以参考这里
其实,useAsyncMemo
的用处远不仅仅是这些。
还记得当年写到脑壳疼的“文本框输入的同时自动搜索”逻辑么?使用useAsyncMemo
,分分钟就可以搞定:
const [input, setInput] = useState()
const users = useAsyncMemo(async () => {
if (input === '') return []
return await apiService.searchUsers(input)
}, [input], [])
甚至还可以加上防抖动的逻辑:
const [input, setInput] = useState()
const [debouncedInput] = useDebounce(input, 300)
const users = useAsyncMemo(async () => {
if (debouncedInput === '') return []
return await apiService.searchUsers(debouncedInput)
}, [debouncedInput], [])
关于useAsyncMemo
的详细介绍,可以在GitHub上查看:
![e38a7233cdde1ecd6f3983f6aea03241.png](https://i-blog.csdnimg.cn/blog_migrate/42535d584e7e053d97516025cca96d6c.jpeg)
(如果觉得好用的话,不妨点个Star~)
如果对useMemo
和useAsyncMemo
有任何疑问的话,也欢迎在评论区中一同探讨~