高阶组件总共分为两大类

  • 代理方式
    1. 操纵prop
    2. 访问ref(不推荐)
    3. 抽取状态
    4. 包装组件
  • 继承方式
    1. 操纵生命周期
    2. 操纵prop

代理方式之 操纵prop

删除prop
import React from 'react'
function HocRemoveProp(WrappedComponent) {
return class WrappingComPonent extends React.Component {
render() {
const { user, ...otherProps } = this.props;
return <WrappedComponent {...otherProps} />
}
}
}
export default HocRemoveProp;

增加prop

import React from 'react'

const HocAddProp = (WrappedComponent,uid) =>
class extends React.Component {
render() {
const newProps = {
uid,
};
return <WrappedComponent {...this.props} {...newProps} />
}
} export default HocAddProp;

上面HocRemoveProp高阶组件中,所做的事情和输入组件WrappedComponent功能一样,只是忽略了名为user的prop。也就是说,如果WrappedComponent能处理名为user的prop,这个高阶组件返回的组件则完全无视这个prop。

const { user, ...otherProps } = this.props;

这是一个利用es6语法技巧,经过上面的语句,otherProps里面就有this.props中所有的字段除了user.
假如我们现在不希望某个组件接收user的prop,那么我们就不要直接使用这个组件,而是把这个组件作为参数传递给HocRemoveProp,然后我们把这个函数的返回结果当作组件来使用
两个高阶组件的使用方法:

const  newComponent = HocRemoveProp(SampleComponent);
const newComponent = HocAddProp(SampleComponent,'1111111');

也可以利用decorator语法糖这样使用

import React, { Component } from 'React';

@HocRemoveProp
class SampleComponent extends Component {
render() {}
}
export default SampleComponent;

代理方式之 抽取状态

将所有的状态的管理交给外面的容器组件,这个模式就是 抽取状态
外面的容器就是这个高阶组件

const HocContainer = (WrappedComponent) =>
class extends React.Component {
constructor(props) {
super(props)
this.state = {
name: ''
}
}
onNameChange = (event) => {
this.setState({
name: event.target.value
})
}
render() {
const newProps = {
name: {
value: this.state.name,
onChange: this.onNameChange
}
}
return <WrappedComponent {...this.props} {...newProps} />
}
}
@HocContainer
class SampleComponent extends React.Component {
render() {
return <input name="name" {...this.props.name}/>
}
}

这样当我们在使用这个已经被包裹的input组件(SampleComponent)时候
它的值就被放在了HocContainer高阶组件中,当很多这样的input组件都用这个HocContainer高阶组件时,那么它们的值都将保存在这个HocContainer高阶组件中

代理方式之 包装组件

const HocStyleComponent = (WrappedComponent, style) =>
class extends React.Component {
render() {
return (
<div style={style}>
<WrappedComponent {...this.props} {...newProps} />
</div>
)
}
}

这样使用

import HocStyleComponent from  './HocStyleComponent';
const colorSytle ={color:'#ff5555'}
const newComponent = HocStyleComponent(SampleComponent, colorSytle);

-代理方式的生命周期的过程类似于堆栈调用:
didmount 一> HOC didmount 一>(HOCs didmount) 一>(HOCs will unmount) 一>HOC will unmount一>unmount

说继承方式之前先看一个例子

const MyContainer = (WrappedComponent) =>
class extends WrappedComponent {
render() {
return super.render();
}
}

这个例子很简单,相当于把WrappedComponent组件的render方法,通过super.render()方法吐到了MyContainer 中,可以顺序调用。

  • 继承方式的生命周期的过程类似于队列调用:
    didmount 一> HOC didmount 一>(HOCs didmount) 一>will unmount一>HOC will unmount一> (HOCs will unmount)

  • 代理方式下WrappedComponent会经历一个完整的生命周期,产生的新组件和参数组件是两个不同的组件,一次渲染,两个组件都会经历各自的生命周期,
  • 在继承方式下,产生的新组件和参数组件合二为一,super.render只是生命周期中的函数,变成一个生命周期。

来看下面的例子你就会明白了。

继承方式之 操纵生命周期(渲染劫持)

首先创建一个高阶,在创建一个使用高阶组件的组件,也就是是输入组件,最后我在改变这个输入组件props

import * as React from 'react';

const HocComponent = (WrappedComponent) =>
class MyContainer extends WrappedComponent {
render() {
if (this.props.time && this.state.success) {
return super.render()
}
return <div>倒计时完成了...</div>
}
}

这个高阶组件会直接读取输入组件中的props,state,然后控制了输入组件的render展示
只有在props.time和state.success同时为真的时候才会展示

import * as React from 'react';
import HocComponent from './HocComponent' @HocComponent class DemoComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
success: true,
};
}
render() {
return <div>我是一个组件</div>
}
}
export default DemoComponent;

然后调用,递减time数值直到变为0

最后页面的效果就是,当然他不是循环的。先展示”我是一个组件“,我设置了两秒,之后展示”倒计时完成“.

由此可以看出高阶组件也可以控制state

但是最好要限制这样做,可能会让WrappedComponent组件内部状态变得一团糟。建议可以通过重新命名state,以防止混淆。

继承方式之 操纵prop

const HOCPropsComponent = (WrappedComponent) =>
class extends WrappedComponent {
render() {
const elementsTree = super.render();
let newProps = {
color: (elementsTree && elementsTree.type === 'div') ? '#fff' : '#ff5555'
}; const props = Object.assign({}, elementsTree.props, newProps)
const newElementsTree = React.cloneElement(elementsTree, props, elementsTree.props.children)
return newElementsTree
}
}

这样就传入了新的props,。
React.cloneElement( element, [props], [...children])
参数:TYPE(ReactElement),[PROPS(object)],[CHILDREN(ReactElement)]
克隆并返回一个新的 ReactElement ,新返回的元素会保留有旧元素的 props、ref、key,也会集成新的 props。

还有一个方式,在传递props上有着强于高阶组件的优势不用关心命名,
class addProps extends React.Component {
render() {
const newProps = 'uid'
return this.props.children(newProps)
}
}

使用方式

<addProps>
{
(argument) => <div>{argument}</div>
}
</addProps>

感觉很方便,但是每次渲染都会重新定义一个新的函数,如果不想的话就不要定义匿名函数,

showUid(argument) {
return <div>{argument}</div>
}
彩蛋recompose库

recompose是一个很流行的库,它提供了很多很有用的高阶组件(小工具),而且也可以优雅的组合它们。

Step 1 扁平props.

我们有这样一个组件

const Profile = ({ user }) => (
<div>
<div>Username: {user.username}</div>
<div>Age: {user.age}</div>
</div>
)

如果想要改变组件接口来接收单个 prop 而不是整个用户对象,可以用 recompose 提供的高 阶组件 flattenProp 来实现。

const Profile = ({ username,age }) => (
<div>
<div>Username: {username}</div>
<div>Age: {age}</div>
</div>
)

const ProfileWithFlattenUser = flattenProp('user')(Profile);
现在我们希望同时使用多个高阶组件:一个用于扁平化处理用户 prop,另一个用于重命名用 户对象的单个 prop,不过串联使用函数的做法似乎不太好。 此时 recompose 库提供的 compose 函数就派上用场了。

const enhance = compose(
flattenProp('user'),
renameProp('username', 'name')
)

然后按照以下方式将它应用于原有组件:

 const EnhancedProfile = enhance(Profile)
还可以将 compose 函数用 在我们自己的高阶组件上,甚至结合使用都可以:
const enhance = compose(
flattenProp('user'),
renameProp('username', 'name'),
withInnerWidth
)
Step 2 提取输入表单的State

我们将从Recompose库中使用withStateHandlers高阶组件。 它将允许我们将组件状态与组件本身隔离开来。 我们将使用它为电子邮件,密码和确认密码字段添加表单状态,以及上述字段的事件处理程序。

import { withStateHandlers, compose } from "recompose";

const initialState = {
email: { value: "" },
password: { value: "" },
confirmPassword: { value: "" }
}; const onChangeEmail = props => event => ({
email: {
value: event.target.value,
isDirty: true
}
}); const onChangePassword = props => event => ({
password: {
value: event.target.value,
isDirty: true
}
}); const onChangeConfirmPassword = props => event => ({
confirmPassword: {
value: event.target.value,
isDirty: true
}
}); const withTextFieldState = withStateHandlers(initialState, {
onChangeEmail,
onChangePassword,
onChangeConfirmPassword
}); export default withTextFieldState;

withStateHandlers它接受初始状态和包含状态处理程序的对象。调用时,每个状态处理程序将返回新的状态。

高阶函数&&高阶组件(二)的更多相关文章

  1. react_结合 redux - 高阶函数 - 高阶组件 - 前端、后台项目打包运行

    Redux 独立的集中式状态管理 js 库 - 参见 My Git 不是 react 库,可以与 angular.vue 配合使用,通常和 react 用 yarn add redux import ...

  2. 高阶组件&&高阶函数(一)

    antd里面的form表单方面,遇到一个高阶函数,以及高阶组件,于是看了一下这方面内容,前辈们的文章写得也非常详细,这里就稍微kobe一下 高阶函数与高阶组件 高阶函数: 高阶函数,是一种特别的函数, ...

  3. 《React后台管理系统实战 :一》:目录结构、引入antd、引入路由、写login页面、使用antd的form登录组件、form前台验证、高阶函数/组件

    实战 上接,笔记:https://blog.csdn.net/u010132177/article/details/104150177 https://gitee.com/pasaulis/react ...

  4. Java函数式编程:二、高阶函数,闭包,函数组合以及柯里化

    承接上文:Java函数式编程:一.函数式接口,lambda表达式和方法引用 这次来聊聊函数式编程中其他的几个比较重要的概念和技术,从而使得我们能更深刻的掌握Java中的函数式编程. 本篇博客主要聊聊以 ...

  5. javascript设计模式学习之三—闭包和高阶函数

    一.闭包 闭包某种程度上就是函数的内部函数,可以引用外部函数的局部变量.当外部函数退出后,如果内部函数依旧能被访问到,那么内部函数所引用的外部函数的局部变量就也没有消失,该局部变量的生存周期就被延续. ...

  6. JavaScript中的高阶函数

    之前写的<JavaScript学习手册>,客户跟我说有些内容不适合初学者,让我删了,感觉挺可惜的,拿到这里和大家分享. JavaScript中的一切都是对象,这句话同样适用于函数.函数对象 ...

  7. js 高阶函数 闭包

    摘自  https://www.cnblogs.com/bobodeboke/p/5594647.html 建议结合另外一篇关于闭包的文章一起阅读:http://www.cnblogs.com/bob ...

  8. 理解运用JS的闭包、高阶函数、柯里化

    JS的闭包,是一个谈论得比较多的话题了,不过细细想来,有些人还是理不清闭包的概念定义以及相关的特性. 这里就整理一些,做个总结. 一.闭包 1. 闭包的概念 闭包与执行上下文.环境.作用域息息相关 执 ...

  9. JS的闭包、高阶函数、柯里化

    本文原链接:https://cloud.tencent.com/developer/article/1326958 https://cloud.tencent.com/developer/articl ...

随机推荐

  1. [状态模式]实现stopwatch

    1.模拟传统面向对象语言的状态模式实现 // Stopwatch类 状态机class Stopwatch {    constructor() {        this.button1 = null ...

  2. 使用CleanWebpackPlugin插件报错原因:CleanWebpackPlugin is not a constructor

    // webpack版本:4.32.2 // 抛错原写法 const CleanWebpackPlugin = require("clean-webpack-plugin"); . ...

  3. 曹工说Spring Boot源码(2)-- Bean Definition到底是什么,咱们对着接口,逐个方法讲解

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码系列开讲了(1)-- Bean Definition到底是什么,附spring思维导图分享 工程代码地址 思维导图地址 工程结构图: 正 ...

  4. 【NPM】使用问题记录

    [NPM]使用问题记录 =========================================================================== 1.MSBUILD : ...

  5. 测试开源.net 混淆器ConfuserEx

    由于公司业务需要简单的把代码加密混淆,于是了解了一下相关的工具然后打算用ConfuserEx试试. 开源地址:https://github.com/yck1509/ConfuserEx/ 下载地址:h ...

  6. elementui入门

    1.前端服务器搭建 (1)创建一个static web project (2) 安装 npm install -g vue-cli (3) vue init webpack 项目名 (4)cd 项目名 ...

  7. python之encode和decode编码

    u = '中文' str3 = u.encode('utf-8') # 以utf-8编码对u进行编码,获得bytes类型对象 print(str3) u2 = str3.decode('utf-8') ...

  8. jdk13-新特性预览

    一新特性 350: Dynamic CDS Archives(动态CDS档案) 351: ZGC: Uncommit Unused Memory(ZGC:取消提交未使用的内存) 353: Reimpl ...

  9. VS2019 开发Django(十一)------表单

    导航:VS2019开发Django系列 今天是中华人民共和国成立70周年的日子,普天同庆,看阅兵看得满腔热血,热泪盈眶,祖国都这么优秀了,我要更加努力才行啊! 这个Django系列的文章,没有很深入的 ...

  10. Ubuntu服务器登录与使用

    1. 登录 从本地登录远程服务器 1.1 默认端口 # format: ssh user_name@ip_address cv@cv: ~$ ssh cv@192.168.1.1 1.2 登录到指定端 ...