本文概述了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模式,咱们看到了利用 "管制反转 "概念的不同形式。它们给你提供了一个弱小的办法来创立灵便和适应性强的组件。
然而,咱们都晓得这句驰名的谚语:"能力越大责任越大",你越是把控制权转移给用户,你的组件就越是远离 "即插即用 "的思维形式。作为一个开发者,你的角色是抉择正确的模式来对应正确的需要。
为了帮忙你实现这项工作,上面的图表依据 "集成的复杂性 "和 "管制反转 "这两个因素对所有这些模式进行了分类。