- 容器模式
- 实现按条件执行Hooks
- 使用render props模式复用UI逻辑
首先,Hooks的一个重要规则:Hooks必须在顶层作用域调用,而不能放在条件判断、循环语句中,即Hooks必须被执行到。
这个规则存在的原因是:React需要在函数组件内部维护所用到的Hooks状态
例子,对于一个对话框组件,通过visible属性来控制是否显示。
比如期望如下:
function UserInfoModal({ visible, userId, ...rest }) {
if (!visible) return null
const [data, loading, error] = useUser(userId)
return (
<Modal visible={visible} {...rest}>
</Modal>
)
}
可以看到,我们期望在visble为false时不显示内容,但是这样却通不过编译,因为useUser这个hook在return语句之后。
所以,我们需要一个使用容器模式来间接的实现条件逻辑
具体做法,就是在两个组件中添加一个条件判断
在UserInfoModal外层加一个容器
export default function UserInfoModalWrapper ({
visible,
...rest // 使用rest获取除visible以外的所有属性
}) {
if (!visible) return null
return <UserInfoModal {...rest} />
}
把判断的条件放到Hooks中去
在容器模式中我们可以看到,条件的隔离对象是多个子组件,这意味着它通常用于一些比较大的逻辑的隔离,对于一些细节的控制,直接放到Hooks中进行判断。
比如当article获取到后再去获取作者。
- 使用render props模式(即render作为props传入)
- react中最重要的一个设计模式,解决UI逻辑复用的问题
- 不仅适用于Class组件,也适用于函数组件
就是把一个render函数作为属性传递给某个组件,由这个组件去执行这个函数从而render实际的内容。
函数组件下,Hooks有一个局限,即只能用作数据逻辑的重用,对于UI表现逻辑的复用,最好使用render props。
首先,实现一个数据逻辑重用的简单例子:
import { useState, useCallback } from 'react'
function CounterRenderProps({ children }) {
cosnt [count, setCount] = useState(0)
const increment = useCallback(() => {
setCount(count+1)
}, [count])
const decrement = useCallback(() => {
setCount(count-1)
},[count])
return children({ count, increment, decrement })
}
function CounterRenderPropsExample() {
<CounterRenderProps>
{
({ count, increment, decrement } => {
return (
<div>
// ...
</div>
)
})
}
</CounterRenderProps>
}
-
有UI逻辑重用的例子
- 比如,我们需要显示一个列表,如果超过一定数量,则把多余的部分折叠起来,通过一个弹出框去显示。
- 实现一个表格,也是显示前五个,多余的折叠到更多里面。
分析一下:
功能相同的部分是,数据超过一定数量,显示一个more的文字,鼠标移上去,则弹出一个框,用于显示其他数据
功能不同的部分:每一个列表项如何渲染,是在使用的时候决定的。
import { Popver } from 'antd'
function ListWithMore({ renderItem, data=[], max }) {
const elements = data.map((item, index) => renderItem(item, index, data))
const show = elements.slice(0,max)
const hide = elements.slice(max)
return (
<span>
{show}
{
hide.length > 0 && (
<Popver>
<span>
and {" "}
<span>{hide.length} more...</span>
</span>
</Popver>
)
}
</span>
)
}
可以看到,组件接收了三个参数,分别是:
- renderItem:用于接收一个函数,由父组件决定如何渲染一个列表项;
- data:需要渲染的数据;
- max:最多显示几条数据。
下面代码展示了上面示意图中两个场景的实现代码: