hello,我是海海。我的微信公众号是:海海前端
这一期我们来讲讲
React Hooks
。阅读时间5~10分钟欢迎转载,请注明原文和作者
有任何不对的地方,欢迎发消息给我
本期大纲:
useEffect
和类组件生命周期useEffect
/useLayoutEffect
/useInsertionEffect
:都是干啥的?useState
和Mobx
:这两者也有关系?useState
和useReducer
:谁更胜一筹?useCallback
和useMemo
:有卵用?
useEffect和类组件生命周期
面试题中常考的
useEffect
类比类组件生命周期
FC
组件第一次渲染之后,触发useEffect
的setUp
回调函数,这里对应componentDidMount
FC
组件二次渲染之后,才会触发useEffect
的setUp
回调函数,这里对应componentDidUpdate
当FC
组件被dom
移除,会调用useEffect
的setUp
回调函数的返回值,这个返回值是一个cleanup
函数。通常用于清理定时器、解除事件绑定、取消订阅等,这里对应componentWillUnMount
需要注意的是:react
组件在重渲染的时候,先使用老的依赖值运行cleanup
,再执行setup
回调函数。参考原文的Parameters的setup部分
useEffect/useLayoutEffect/useInsertionEffect:都是干啥的?
什么?你还没听过这三个
hooks
、是时候表演真正的技术了!
比较点 | useEffect | useLayoutEffect | useInsertionEffect |
---|---|---|---|
组件渲染 | 之后 | 之后 | 之前 |
浏览器repaint | 之后 | 之前 | 之前 |
啥时候用? | 大部分的不会立即改变ui的场景: fetch, eventlistener | 发生UI闪烁时 | css in js |
css in js: 就是在js中动态的创建css样式
useState和Mobx:这两者也有关系?
是不是常常在组件里面到处用
useState
?或者别人写的useState
太多,你想点击离职按钮?没关系,让我们使用Mobx
来解决这个问题
Mobx
(Mobx State Tree)是一个用于状态管理的JavaScript
库。那么他能为useState
做些什么呢?
- 可维护:将组件的
state
抽离到Mobx
中 - 响应式:用
observer
包裹组件,实现数据响应式
我们看一个Mobx
结合Context
的例子:
Store
import { createContext, useContext } from 'react'
import { makeAutoObservable } from 'mobx'
class Store {
constructor() {
makeAutoObservable(this)
}
name: string
setName(name) {
this.name = name
}
}
export const StoreContext = createContext<Store | null>(null)
export const useStore = () => {
const store = useContext(StoreContext)
if (!store) {
throw new Error('useStore must be used in StoreProvider')
}
return store
}
export default Store
Father组件:使用
StoreContext
和Store
import Children from './Children.tsx'
import Store, { StoreContext } from './store'
const Father: FC<IProps> = (props) => {
const store = useLocalObservable(() => new Store())
reutrn (
<StoreContext.Provider value={store}>
<Children></Children>
</StoreContext.Provider >
)
}
export default observer(Father)
Children组件:使用
useStore
import { useStore } from './store'
const Children: FC<IProps> = (props) => {
const { name } = useStore()
return (
<span>{name}</span>
)
}
export default observer(Index)
useState和useReducer:谁更胜一筹?
除
Mobx
,我们还可以使用useReducer
代替useState
组件state比较少的情况下,用useReducer,会增加复杂度。而当state很多时,并且状态之间会有关联的情况下,可以使用useReducer。
import { useReducer } from 'react'
const reducer = (state, action) => {
switch (action.type) {
case 'signIn': {
...
return
}
case 'signOut': {
...
return
}
default: return
}
}
const Index: FC<IProps> = (props) => {
const [loginInfo, dispatch] = useReducer(reducer, {
userName: '',
password: ''
})
return (
<div>
<button onClick={dispatch({ type: 'signIn' })}>登录</button>
<button onClick={dispatch({ type: 'signOut' })}>登出</button>
</div>
)
}
需要注意的是:
useReducer
的reducer
函数必须是纯函数- 第三个参数是一个函数,用
init(initialArg)
来设置initialArg
,如果未指定,则用initialArg
- 在严格模式下,React 会调用你的reducer和init两次,其中一个调用结果将被忽略
action
是用户执行的操作,它可以是任何类型的值。按照约定,操作通常是一个对象,其属性标识它,type
也可以选择具有附加信息的其他属性- 如果在调用 dispatch 函数后读取状态变量,则仍将获得调用前屏幕上的旧值
useCallback和useMemo:有卵用?
当你的面试官问你:“确实,按照理论来说,这两个
hooks
是这样用的。但是,你知道他们的实际应用场景是怎么样的吗?”“他们在一般情况下,没有任何卵用。所以可以删除他。”你自信的回答道。
你的面试官满意的笑了笑,说:“你被录用了”。
让我们来看看useCallback
和useMemo
到底是干嘛的?
一般来说,useCallback
是用来缓存匿名函数的,useMemo
是用来缓存对象或者组件的。
Well,这只是第一步。
让我们看看实际的情况下,大多数的组件是否会按照我们的设想,缓存组件和函数。
App组件
const App = () => {
const [count, setCount] = useState<number>(0)
return (
<div>
<Father count={count} />
<button onClick={() => setCount(count + 1)}>点击添加</button>
</div>
)
}
Father组件
interface IProps {
count: number
}
const Father: FC<IProps> = (props) => {
const [name, setName] = useState<string>('')
const setVisible = useCallback(() => {})
const Son = useMemo(() => {
return <div></div>
}, [])
useEffect(() => {
setName(海海 + count)
}, [props.count])
return (
<div>
<Children setVisible={setVisible} name={name} />
<Son />
</div>
)
}
上面的Father
组件中缓存了一个setVisible
函数给Children
组件,缓存了一个Son
组件。当我们点击App
组件的“点击添加”按钮时,会发生什么?
App
组件的count
改变,触发App组件重渲染- 递归重渲染
Father
组件,注意到,虽然我们缓存了Son
,但是Father
因为count
变了,整个函数便被重新执行,于是useMemo
失效了 - 同时由于
name
会因为count
的改变而改变,所以Children
组件也会被重新渲染,那么之前缓存的setVible
会被重新执行,于是useCallback
失效了
只有当前组件的所有属性都被缓存时,才可以使用useCallback缓存函数
只有当父组件的所有属性都被缓存时,才可以使用useMemo缓存子组件或对象
感谢你的耐心阅读,如果觉得好的话,可以给我点个赞吗