Skip to content

组件设计模式(4):提供者模式

作者:Long Mo
字数统计:1k 字
阅读时长:3 分钟

问题场景

在 React 中,props 是组件之间通讯的主要手段,但是,有一种场景单纯靠 props 来通讯是不恰当的,那就是两个组件之间间隔着多层其他组件

在 React 中,解决这个问题应用的就是“提供者模式”。

提供者模式

虽然这个模式叫做“提供者模式”,但是其实有两个角色,一个叫“提供者”(Provider),另一个叫“消费者”(Consumer),这两个角色都是 React 组件。

其中“提供者”在组件树上居于比较靠上的位置,“消费者”处于靠下的位置。

既然名为“提供者”,它可以提供一些信息,而且这些信息在它之下的所有组件,无论隔了多少层,都可以直接访问到,而不需要通过 props 层层传递。

避免 props 逐级传递,即是提供者的用途

如何实现提供者模式

实现提供者模式,需要 React 的 Context 功能,可以说,提供者模式只不过是让 Context 功能更好用一些而已。

所谓 Context 功能,就是能够创造一个“上下文”,在这个上下文笼罩之下的所有组件都可以访问同样的数据。

在 React v16.3.0 之前,React 虽然提供了 Context 功能,但是官方文档上都建议尽量不要使用,因为对应的 API 他们并不满意,觉得迟早要废弃掉。

即使如此,依然有很多库和应用使用 Context 功能,可见对这个需求的呼声有多大。

当 React 发布 v16.3.0 时,终于提供了“正式版本”的 Context 功能 API,和之前的有很大不同,当然,这也带来一些问题,我在后面会介绍。

提供者模式的一个典型用例就是实现 “样式主题”(Theme),由顶层的提供者确定一个主题,下面的样式就可以直接使用对应主题里的样式。

这样,当需要切换样式时,只需要修改提供者就行,其他组件不用修改。

React v16.3.0 之后的提供者模式

到了 React v16.3.0 的时候,新的 Context API 出来了,这套 API 毫不掩饰自己就是“提供者模式”的实现,命名上就带 “Provider” 和 “Consumer”。

还是上面的样式主题的例子,首先,要用新提供的 createContext 函数创造一个“上下文”对象。

jsx
const ThemeContext = React.createContext();

这个“上下文”对象 ThemeContext 有两个属性,分别就是——对,你没猜错——Provider 和 Consumer。

jsx
const ThemeProvider = ThemeContext.Provider;
const ThemeConsumer = ThemeContext.Consumer;

创造“提供者”极大简化了,都不需要我们创造一个 React 组件类。

两种提供者模式实现方式的比较

通过上面的代码,可以很清楚地看到,新的 Context API 更简洁,但是,也并不是十全十美。

在老版 Context API 中,“上下文”只是一个概念,并不对应一个代码,两个组件之间达成一个协议,就诞生了“上下文”。

在新版 Context API 中,需要一个“上下文”对象(上面的例子中就是 ThemeContext),

使用“提供者”的代码和“消费者”的代码往往分布在不同的代码文件中,那么,这个 ThemeContext 对象放在哪个代码文件中呢?

最好是放在一个独立的文件中,这么一来,就多出一个代码文件,而且所有和这个“上下文”相关的代码,都要依赖于这个“上下文”代码文件, 虽然这没什么大不了的,但是的确多了一层依赖关系。

为了避免依赖关系复杂,每个应用都不要滥用“上下文”,应该限制“上下文”的使用个数。

不管怎么说,新版本的 Context API 才是未来,在 React v17 中,可能就会删除对老版 Context API 的支持,所以,现在大家都应该使用第二种实现方式

Contributors

Long Mo
文章作者:Long Mo
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Longmo Docs