面向初学者的高阶组件介绍
作者:Brandon Newton
原文:Higher-Order Components (HOCs) for Beginners
谈点:一篇面向初学者的 HOC 介绍。高阶组件听起来挺唬人的,只看名字恐怕不是那么容易明白究竟是何物,而且通常来讲高阶组件并不是组件,而是接受组件作为参数,并且返回组件的函数。早期利用 ES5 的 mixin 语法来做的事,基本都可以使用高阶组件代替,而且能做的还有更多。
前言
写这篇文章的起因是其他关于高阶组件(Higher-Order Components)的文章,包含官方文档,都令初学者感到相当困惑。我知道有高阶组件这样一个东西,但不知道它到底有什么用。所以,想通过一篇文章来对高阶组件有一个更好的理解。
在此之前,我们需要先来讲一下 JavaScript 中的函数。
ES6 箭头函数简介
接下来将提供一些箭头函数的简单示例,如果之前没有使用过,可以认为它们与普通函数基本一致。下面的代码会展示箭头函数与普通函数的区别。
function () {return 42
}// same as:
() => 42// same as:
() => {return 42
}function person(name) {return { name: name }
}// same as:
(name) => {return { name: name }
}
阅读 MDN 的箭头函数文档 了解更多信息。
作为值的函数与部分调用
就像是数字、字符串、布尔值 一样,函数也是值,意味着可以像传递其他数据一样传递函数,可以将函数作为参数传递给另外一个函数。
const execute = (someFunction) => someFunction()execute(() => alert('Executed'))
也可以在在函数中返回一个函数:
const getOne = () => () => 1getOne()()
之所以在 getOne
后面有两个 ()
,是因为第一个返回的返回值是一个函数。如下:
const getOne = () => () => 1getOne
//=> () => () => 1getOne()
//=> () => 1getOne()()
//=> 1
从函数返回函数可以帮助我们追踪初始输入函数。例如,下面的函数接受一个数字作为参数,并返回一个将该参数乘以新参数的函数:
const multiply = (x) => (y) => x * ymultiply(5)(20)
这个示例跟上述 getOne
一样,在下面这个例子,让 x = 5,y = 20。
const multiply = (x) => (y) => x * ymultiply
//=> (x) => (y) => x * ymultiply(5)
//=> (y) => 5 * ymultiply(5)(20)
//=> 5 * 20
在只传入一个参数调用 multiply
函数时,即部分调用该函数。比如,multiply(5)
讲得到一个将其输入值乘以 5 的函数,multiply(7)
将得到一个将其输入值乘以 7 的函数。依此类推。通过部分调用可以创建一个预定义功能的新函数:
const multiply = (x) => (y) => x * yconst multiplyByFive = multiply(5)
const multiplyBy100 = multiply(100)multiplyByFive(20)
//=> 100
multiply(5)(20)
//=> 100multiplyBy100(5)
//=> 500
multiply(100)(5)
//=> 500
一开始看起来似乎没什么用,但是,通过部分调用这种方式可以编写可读性更高,更易于理解的代码。举个例子,可以用一种更清晰的方式来代替 style-components 的函数插入语法。
// before
const Button = styled.button`background-color: ${({ theme }) => theme.bgColor}color: ${({ theme }) => theme.textColor}
`<Button theme={themes.primary}>Submit</Button>
// after
const fromTheme = (prop) => ({ theme }) => theme[prop]const Button = styled.button`background-color: ${fromTheme("bgColor")}color: ${fromTheme("textColor")}
`<Button theme={themes.primary}>Submit</Button>
我们创建一个接受一个字符串作为参数的函数 fromTheme("textColor")
:它返回一个接受具有 theme
属性的对象的函数:({ theme }) => theme[prop]
,然后再通过初始传入的字符串 "textColor"
进行查找。我们可以做得更多,写类似的 backgroundColor
和 textColor
这种部分调用 fromTheme
的函数:
const fromTheme = (prop) => ({ theme }) => theme[prop]
const backgroundColor = fromTheme("bgColor")
const textColor = fromTheme("textColor")const Button = styled.button`background-color: ${backgroundColor}color: ${textColor}
`<Button theme={themes.primary}>Submit</Button>
高阶函数
高阶函数的定义是,接受函数作为参数的函数。如果曾经使用过类似 map 这样的函数,可能已经很熟悉高阶函数。如果不熟悉 map,它是一个数组遍历的方法,接受一个函数作为参数应用到数组中的每个元素。例如,可以像这样对一个数组作平方:
const square = (x) => x * x[1, 2, 3].map(square)
//=> [ 1, 4, 9 ]
可以实现一个我们自己的 map
版本来说明这个概念:
const map = (fn, array) => {const mappedArray = []for (let i = 0; i < array.length; i++) {mappedArray.push(// apply fn with the current element of the arrayfn(array[i]))}return mappedArray
}
然后再使用我们的 map 版本来对一个数组作平方:
const square = (x) => x * xconsole.log(map(square, [1, 2, 3, 4, 5]))
//=> [ 1, 4, 9, 16, 25 ]
译者注:我们也可以将 map 方法从对象中解耦出来:
const map = (fn, array) => Array.prototype.map.call(array, fn)
这样也可以像上述例子一样调用。
或者更函数式的做法,再来点柯里化:const map = array => fn => Array.prototype.map.call(array, fn)
或者是返回一个 <li>
的 React 元素数组:
const HeroList = ({ heroes }) => (<ul>{map((hero) => (<li key={hero}>{hero}</li>), heroes)}</ul>
)<HeroList heroes=["Wonder Woman","Black Widow","Spider Man","Storm","Deadpool"
]/>
/*=> (<ul><li>Wonder Woman</li><li>Black Widow</li><li>Spider Man</li><li>Storm</li><li>Deadpool</li></ul>
)*/
高阶组件
我们知道,高阶函数是接受函数作为参数的函数。在 React 中,任何返回 JSX 的函数都被称为无状态函数组件,简称为函数组件。基本的函数组件如下所示:
const Title = (props) => <h1>{props.children}</h1><Title>Higher-Order Components(HOCs) for React Newbies</Title>
//=> <h1>Higher-Order Components(HOCs) for React Newbies</h1>
高阶组件则是接受组件作为参数并返回组件的函数。如何使用传入组件完全取决于你,甚至可以完全忽视它:
// Technically an HOC
const ignore = (anything) => (props) => <h1>:)</h1>const IgnoreHeroList = ignore(HeroList)
<IgnoreHeroList />
//=> <h1>:)</h1>
可以编写一个将输入转换成大写的 HOC:
const yell = (PassedComponent) =>({ children, ...props }) =><PassedComponent {...props}>{children.toUpperCase()}!</PassedComponent>const Title = (props) => <h1>{props.children}</h1>
const AngryTitle = yell(Title)<AngryTitle>Whatever</AngryTitle>
//=> <h1>WHATEVER!</h1>
你也可以返回一个有状态组件,因为 JavaScript 中的类不过是函数的语法糖。这样就可以使用到 React 生命周期的方法,比如 componentDidMount
。这是 HOCs 真正有用的地方。我们现在可以做一些稍微有趣点的事,比如将 HTTP 请求的结果传递给函数组件。
const withGists = (PassedComponent) =>class WithGists extends React.Component {state = {gists: []}componentDidMount() {fetch("https://api.github.com/gists/public").then((r) => r.json()).then((gists) => this.setState({gists: gists}))}render() {return (<PassedComponent{...this.props}gists={this.state.gists}/>)}}const Gists = ({ gists }) => (<pre>{JSON.stringify(gists, null, 2)}</pre>
)const GistsList = withGists(Gists)<GistsList />
//=> Before api request finishes:
// <Gists gists={[]} />
//
//=> After api request finishes:
// <Gists gists={[
// { /* … */ },
// { /* … */ },
// { /* … */ }
// ]} />
withGists
会传递 gist api 调用的结果,并且你可以在任何组件上使用。点击这里 可以看到一个更加完整的例子。
结论:高阶组件是 ???
react-redux 也是使用 HOC, connect 将应用 store 的值传递到“已连接” 的组件。它还会执行一些错误检查和组件生命周期优化,如果手动完成将导致编写大量重复代码。
如果你发现自己在不同地方编写了大量的代码,那么也可以将代码重构成可重用的 HOC。
HOCs 非常具有表现力,可以使用它们创造很多很酷的东西。
尽可能地保持你的 HOC 简单,不要编写需要阅读长篇大论才能理解的代码。
附加练习
下面有一些练习,来巩固对 HOC 的理解:
- 写一个反转其输入的 HOC
- 编写一个HOC,将 API 中的数据提供给组件
- 写一个HOC来实现
shouldComponentUpdate
,以避免更新。 - 编写一个 HOC,使用
React.Children.toArray
对传入组件子元素进行排序。
面向初学者的高阶组件介绍相关推荐
- [vue] 你了解什么是高阶组件吗?可否举个例子说明下?
[vue] 你了解什么是高阶组件吗?可否举个例子说明下? 高阶组件 高阶组件介绍 vue 高阶组件的认识,在React中组件是以复用代码实现的,而Vue中是以mixins 实现,并且官方文档中也缺少一 ...
- React总结篇之六_React高阶组件
高阶组件的概念及应用 以函数为子组件的模式 这两种方式的最终目的都是为了重用代码,只是策略不同,各有优劣,开发者可以在实际工作中决定采用哪种方式. 一.高阶组件 1. 高阶组件(Higher Orde ...
- 组件和高阶组件区别_为什么要对高阶组件使用代码拆分
组件和高阶组件区别 by Nitish Phanse 由Nitish Phanse 为什么要对高阶组件使用代码拆分 (Why you should use code splitting with hi ...
- 高阶组件HOC - 小试牛刀
原文地址:https://github.com/SmallStoneSK/Blog/issues/6 1. 前言 老毕曾经有过一句名言,叫作"国庆七天乐,Coding最快乐~".所 ...
- 《信息物理融合系统(CPS)设计、建模与仿真——基于 Ptolemy II 平台》——2.7 高阶组件...
本节书摘来自华章出版社<信息物理融合系统(CPS)设计.建模与仿真--基于 Ptolemy II 平台>一书中的第2章,第2.7节,作者:[美]爱德华·阿什福德·李(Edward Ashf ...
- 「react进阶」一文吃透React高阶组件(HOC)
一 前言 React高阶组件(HOC),对于很多react开发者来说并不陌生,它是灵活使用react组件的一种技巧,高阶组件本身不是组件,它是一个参数为组件,返回值也是一个组件的函数.高阶作用用于强化 ...
- React高阶组件(HOC)
你可以想象,在一个大型应用程序中,这种订阅DataSource和调用setState的模式将一次又一次地发生.我们需要一个抽象,允许我们在一个地方定义这个逻辑,并在许多组件之间共享它.这正是高阶组件擅 ...
- React高阶组件探究
React高阶组件探究 在使用React构建项目的过程中,经常会碰到在不同的组件中需要用到相同功能的情况.不过我们知道,去这些组件中到处编写同样的代码并不优雅. 在以往,为了解决以上的情况,我们会用到 ...
- 写一个组织架构图组件来深入认识vue函数式高阶组件
本文涉及到的知识点: Vue函数式组件 递归函数 深拷贝对象 正则匹配 近期在开发一个vue组织架构图组件时,为了实现高性能渲染和一些特殊用法,使用了函数式组件,要实现的效果是这样: 写一个组织架构图 ...
最新文章
- SpringMVC中实现的token,防表单重复提交
- USB2.0 设备类代码表
- awk 数组用法【精华贴】
- (转)用ASP.NET向Javascript传递变量 方法1:
- from import 导入时找不到module的解决办法(Python模块包中_init_.py文件的作用)
- 【CodeForces - 124C】Prime Permutation(数学,思维,小结论)
- Java Socket编程中使用ObjectOutputStream 和 ObjectInputStream 出现问题
- Maven实战 | dependencies与dependencyManagement
- 携号转网可能只会叫好不叫座
- 第 19 次 CCF CSP 认证 202006-2 稀疏向量(svector)
- 生活中的实验 —— 家庭电路
- 第六章 使用ADO.NET查询和操作数据
- 苹果Mac鼠标光标丢失如何找回?
- oracle读取blob字段的方法,如何读取Oracle的BLOB字段里的文件?
- 可信人工智能白皮书(概要)
- 安卓平板python编程软件下载_10 个可以在平板电脑上使用的 Python 编辑器
- 太极图正确画法_太极图唯一正确的画法
- Java脚本写的随机验证码
- vue v-model 双向绑定表单元素的数据:实质是绑定了value、checked、selected属性
- 闲聊flask web编程中的cookie
热门文章
- 数字时代,互联网企业的组织形态
- 面经——oppo2022校招Linux系统工程师
- 脾胃不好,有哪些在家可以做的暖胃汤方?
- 上传图片到php服务器
- DateAdd 函数
- 【Camunda六】Camunda实现会签和或签
- activiti会签多人审批(通过以及驳回)
- remote: pingfan443: Incorrect username or password (access token) fatal: Authentication failed for
- 什么是HBA(Host Bus Adapter,HBA)
- ES6-Promise实时获取地址和天气问题