本文概述了 5 种古代高级 React 模式,包含集成代码、长处和毛病,以及在公共库中的具体用法。
像每个 React 开发者一样,你可能曾经问过本人以下问题之一
- 我如何建设一个 可重复使用 的组件以适应不同的应用状况?
- 我如何建设一个具备 简略 API的组件,使其易于应用?
- 我如何建设一个在用户界面和性能方面 可扩大 的组件?
这些重复呈现的问题催生了整个 React 社区的一些高级模式的呈现
在这篇文章中,咱们将看到 5 种不同模式的概述。为了便于比拟,咱们将对所有这些模式应用一个雷同的构造。
咱们将从一个小的介绍开始,而后是一个实在的代码例子(基于同一个简略的 Counter
组件)。
咱们将列出长处和毛病,而后在一个名为 “ 规范 ” 的局部中定义两个因素。
- 反转管制: 你的组件给用户提供的灵活性和管制等级
- 施行的复杂性: 你和用户实现该模式的难度。
最初,咱们将找一些公共库在生产环境中应用该模式的例子
在这篇文章中,咱们将思考一个 React 开发者(你)为其余开发者构建一个组件的状况。因而,” 用户 ” 这个角色间接指的是这些开发者(而不是应用你的网站 / 应用程序的最终用户)。
1. 复合组件模式(Compound Components Pattern)
这种模式容许创立富裕表现力和申明性的组件,防止非必要的 prop drilling。如果你想让你的组件更有可塑性,有更好的关注点拆散和易了解的 API,你应该思考应用这种模式。
例子
import React from "react";
import {Counter} from "./Counter";
function Usage() {const handleChangeCounter = (count) => {console.log("count", count);
};
return (<Counter onChange={handleChangeCounter}>
<Counter.Decrement icon="minus" />
<Counter.Label>Counter</Counter.Label>
<Counter.Count max={10} />
<Counter.Increment icon="plus" />
</Counter>
);
}
export {Usage};
长处
- 缩小了 API 的复杂性:与其把所有的
props
都塞进一个微小的父组件中,而后再把这些props
钻到子 UI 组件中,不如在这里把每个props
都连贯到各自最有意义的子组件上。
- 灵便的标记构造:你的组件有很大的 UI 灵活性,容许从一个繁多的组件创立各种状况。例如,用户能够扭转子组件的程序或定义哪个组件应该被显示。
- 关注点拆散:大部分的逻辑都蕴含在主
Counter
组件中,而后用React.Context
来分享所有子组件的状态和事件处理。咱们失去了一个明确的责任划分。
毛病
-
太高的 UI 灵活性:领有灵活性的同时,也有可能引发意想不到的行为(把一个不须要的组件的子组件放进去,把子组件的程序弄乱,遗记蕴含一个必须的子组件)
依据你想要用户如何应用你的组件,你可能不心愿有那么多的灵活性。
- 更重的 JSX:利用这种模式会减少 JSX 行的数量,特地是当你应用像
ESLint
这样的代码检测工具或相似Prettier
这样的代码格式化工具时
在单个组件的规模上,这仿佛不是什么大问题,但当你从全局来看时,必定会产生微小的差别。
规范
- 反转管制:1/4
- 施行的复杂性:1/4
应用此模式的公共库
- React Bootstrap
- Reach UI
2. 受控属性模式
这种模式将你的组件转变为一个受控组件。内部状态作为 “ 繁多事实源 “ 被耗费,容许用户插入自定义逻辑,批改默认组件的行为。
例子
import React, {useState} from "react";
import {Counter} from "./Counter";
function Usage() {const [count, setCount] = useState(0);
const handleChangeCounter = (newCount) => {setCount(newCount);
};
return (<Counter value={count} onChange={handleChangeCounter}>
<Counter.Decrement icon={"minus"} />
<Counter.Label>Counter</Counter.Label>
<Counter.Count max={10} />
<Counter.Increment icon={"plus"} />
</Counter>
);
}
export {Usage};
长处
- 给予更多的管制:因为主状态裸露在你的组件之外,用户能够管制它,因而能够间接影响你的组件。
毛病
- 施行的复杂性: 之前,在一个中央(
JSX
)的一个集成就足以使你的组件工作。当初,它将扩散在 3 个不同的中央(JSX
/useState
/handleChange
)。
参考 React 实战视频解说:进入学习
规范
- 反转管制:2/4
- 施行的复杂性:1/4
应用此模式的公共库
- Material UI
3. 自定义钩子模式
让咱们在 “ 管制反转 “ 中更进一步:次要的逻辑当初被转移到一个自定义的钩子中。这个钩子能够被用户拜访,并且裸露了几个外部逻辑(状态、处理程序),容许他对你的组件有更好的管制。
例子
import React from "react";
import {Counter} from "./Counter";
import {useCounter} from "./useCounter";
function Usage() {const { count, handleIncrement, handleDecrement} = useCounter(0);
const MAX_COUNT = 10;
const handleClickIncrement = () => {
//Put your custom logic
if (count < MAX_COUNT) {handleIncrement();
}
};
return (
<>
<Counter value={count}>
<Counter.Decrement
icon={"minus"} onClick={handleDecrement}
disabled={count === 0}
/>
<Counter.Label>Counter</Counter.Label>
<Counter.Count />
<Counter.Increment
icon={"plus"} onClick={handleClickIncrement}
disabled={count === MAX_COUNT}
/>
</Counter>
<button onClick={handleClickIncrement} disabled={count === MAX_COUNT}>
Custom increment btn 1 </button>
</>
);
}
export {Usage};
长处
- 给予更多的管制: 用户能够在钩子和 JSX 元素之间插入本人的逻辑,容许他批改默认组件的行为。
毛病
- 施行的复杂性:因为逻辑局部与渲染局部是离开的,所以必须由用户将两者分割起来。要正确地实现它,须要对你的组件的工作形式有一个很好的了解。
规范
- 反转管制:2/4
- 施行的复杂性:2/4
应用此模式的公共库
- React table
- React hook form
4. Props getter 模式
自定义钩子模式提供了很好的管制,但也使你的组件更难集成,因为用户必须解决大量的组件本地钩子的 props
,并在他那边从新创立逻辑。Props Getters 模式试图覆盖这种复杂性。咱们不裸露本地props
,而是提供一个props getters
的短名单。一个getter
是一个返回许多 props
的函数,它有一个有意义的名字,容许用户天然地将其链接到正确的 JSX 元素。
例子
import React from "react";
import {Counter} from "./Counter";
import {useCounter} from "./useCounter";
const MAX_COUNT = 10;
function Usage() {
const {
count,
getCounterProps,
getIncrementProps,
getDecrementProps
} = useCounter({
initial: 0,
max: MAX_COUNT
});
const handleBtn1Clicked = () => {console.log("btn 1 clicked");
};
return (
<>
<Counter {...getCounterProps()}>
<Counter.Decrement icon={"minus"} {...getDecrementProps()} />
<Counter.Label>Counter</Counter.Label>
<Counter.Count />
<Counter.Increment icon={"plus"} {...getIncrementProps()} />
</Counter>
<button {...getIncrementProps({ onClick: handleBtn1Clicked})}>
Custom increment btn 1 </button>
<button {...getIncrementProps({ disabled: count > MAX_COUNT - 2})}> Custom increment btn 2 </button>
</>
);
}
export {Usage};
长处
- 易用性:提供一种简略的形式来整合你的组件,复杂性被暗藏起来,用户只需将正确的 getter 连贯到正确的 JSX 元素。
- 灵活性:用户依然有可能重载
getters
中的props
,以适应他的具体情况。
毛病
- 短少可见性:
getters
带来的抽象性使你的组件更容易集成,但也更不通明和 “ 魔法 ”。为了正确地笼罩你的组件,用户必须晓得getters
所裸露的props
列表,以及如果其中一个props
被扭转所带来的外部逻辑影响。
规范
- 反转管制:3/4
- 集成的复杂性:3/4
应用此模式的公共库
- React table
- Downshift
5. State reducer 模式
在管制的反转方面是最先进的模式。它为用户提供了一种先进的形式来扭转你的组件的外部操作形式。
代码相似于自定义钩子模式,但除此之外,用户还定义了一个被传递给钩子的 reducer
。这个reducer
将重载你的组件的任何外部动作。
例子
import React from "react";
import {Counter} from "./Counter";
import {useCounter} from "./useCounter";
const MAX_COUNT = 10;
function Usage() {const reducer = (state, action) => {switch (action.type) {
case "decrement":
return {count: Math.max(0, state.count - 2) //The decrement delta was changed for 2 (Default is 1)
};
default:
return useCounter.reducer(state, action);
}
};
const {count, handleDecrement, handleIncrement} = useCounter({ initial: 0, max: 10},
reducer
);
return (
<>
<Counter value={count}>
<Counter.Decrement icon={"minus"} onClick={handleDecrement} />
<Counter.Label>Counter</Counter.Label>
<Counter.Count />
<Counter.Increment icon={"plus"} onClick={handleIncrement} />
</Counter>
<button onClick={handleIncrement} disabled={count === MAX_COUNT}>
Custom increment btn 1 </button>
</>
);
}
export {Usage};
在这个例子中,咱们联合了 State reducer 模式和 Custom hook 模式,然而你也能够把它和 Compound components 模式一起应用,间接把 reducer 传递给主组件 Counter。
长处
- 给予更多的管制:在最简单的状况下,应用
state reducers
是把控制权留给用户的最好办法。你所有的外部组件的动作当初都能够从内部拜访,并且能够被重写。
毛病
- 施行的复杂性:这种模式的实现必定是最简单的,无论是对你还是对用户。
- 短少可见性:因为任何
reducer
的动作都能够被扭转,因而须要很好地了解组件的外部逻辑。
规范
- 反转管制:4/4
- 集成的复杂性:4/4
应用此模式的公共库
- Downshift
总结
通过这 5 个高级 React 模式,咱们看到了利用 “ 管制反转 “ 概念的不同形式。它们给你提供了一个弱小的办法来创立灵便和适应性强的组件。
然而,咱们都晓得这句驰名的谚语:” 能力越大责任越大 ”,你越是把控制权转移给用户,你的组件就越是远离 “ 即插即用 “ 的思维形式。作为一个开发者,你的角色是抉择正确的模式来对应正确的需要。
为了帮忙你实现这项工作,上面的图表依据 “ 集成的复杂性 “ 和 “ 管制反转 “ 这两个因素对所有这些模式进行了分类。