函数组件设计模式
保证状态的唯一数据源,语义化的拆分复杂组件都是一些设计模式。
容器模式:实现按条件执行 Hooks
注意点:Hooks 必须在顶层作用域调用(因为 React 需要在函数组件内部维护所用到的 Hooks 的状态)。
比如有一个对话框组件,我们只想要它在组件出现时执行业务逻辑,那么就可以在 Hook 内部判断如果不显示则返回 null。
function UserModal(visible) {
if(!visible) return null;
useEffect(......)
return <div>...</div>
}
但是显然这样是无法通过编译的,因为在 return 之后使用了 useEffect 这个 Hook。
所以这里需要用到一个容器模式,就是把条件贩毒案的结果放在两个组件之中,确保真正的 render UI 的组件收到的所有属性都是有值的。
function UserModalWrapper(visible) {
if (!visible) return null;
return <UserModal visible />;
}
就是在原来的函数组件外增加一个容器,这样就可以实现对应的条件渲染。
这样的好处是可以在根组件下减少条件判断语句,可以确保每个组件尽量短小,更便于阅读和维护。
也可以将一些非 render UI 相关的逻辑自包含进 hooks 内。
使用 render props 模式重用 UI 逻辑
把一个 render 函数作为属性传递给某个组件,由这个组件去执行这个函数从而 render 实际的内容。
在函数组件内 hooks 只能用作数据逻辑的重用,而涉及到 UI 就有些麻烦,这时候就可以尝试 render props 这个设计模式。
使用 render props 如何封装一个计数器,让他可以在不同的组件内使用。
function CounterRenderProps({ children }) {
const [count, setCount] = useState(0);
const change = useCallback(
(step = 1) => {
setCount(count + step);
},
[count]
);
return children({ count, change });
}
// 调用时
function Counter() {
return (
<CounterRenderProps>
{({ count, change }) => {
return <div>....</div>;
}}
</CounterRenderProps>
);
}
这里就是利用了 children 的这个特殊属性,也就是组件开始 tag 和结束 tag 之间的内容其实都会作为 children 这个属性传递给组件的。
很显然使用 Hooks 的方式是更简洁的,但是还是需要掌握 Render props 这种设计模式。
这里需要研究老师给出的这个 ListWithMore 的例子。