高阶函数
高阶组件(属性代理)
- 普通组件还可以向高阶组件传值
- 使用情景:
- 几个页面的布局排版非常相似(只是某一、多个地方根据自身类而展示的不同),可以使用高阶组件进行都一致的布局,A/B/C...等类传入各自不同的类作为子类。(另外的做法就是组件全部分割为单独的类,A/B/C...等进行多个小类的组合成为一个完整的类)
- 高阶组件作为基类存在
高阶组件(反向继承)
普通组件的 static 方法怎么传入高阶组件内部
- 使用情景:
- 拦截组件的生命周期函数及修改state
作用:
- 更改Props(高阶组件(属性代理))
在返回的新组件上传入新的props- 组合组件(高阶组件(属性代理))
在返回的新组件上可以包裹其他组件,进行多个组件的组合- 修改state(高阶组件(反向继承))
继承于父类,可以修改父类的state- 渲染劫持(高阶组件(反向继承))
继承于父类,在渲染父类之前,可以先走子类的逻辑
先说下高阶函数~
高价函数:一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数
-
先写下普通的函数
数据
this.data = {
name: '张三',
age: '20',
sex: '男',
address: '北京通州区',
company: '北京朝阳区'
}
A、B 两个页面都要对同一个数据进行操作,不同的是输出不一样。
这样写的话两个页面会存在同样的代码,把 info 当做更加复杂的数据处理,代码冗余会特别多。假如现在又加了一个C页面,同样的数据不同的操作......
A页面
welcome(){
let info = "姓名:" + this.data.name + "\n" +
"年龄:" + this.data.age + "\n" +
"性别:" + this.data.sex + "\n" +
"住址:" + this.data.address + "\n" +
"公司:" + this.data.company
console.log('welcome:\n',info)
}
B页面
goodBye(){
let info = "姓名:" + this.data.name + "\n" +
"年龄:" + this.data.age + "\n" +
"性别:" + this.data.sex + "\n" +
"住址:" + this.data.address + "\n" +
"公司:" + this.data.company
console.log('goodBye:\n',info)
}
A页面
this.welcome()
B页面
this.goodBye()
高阶函数登场~ 蹬蹬蹬蹬蹬蹬蹬蹬~ 丢丢丢~
接受一个函数作为参数,返回一个新的函数
welcome(info){
console.log('welcome:\n',info)
}
goodBye(info){
console.log('goodBye:\n',info)
}
// 高阶函数 传入函数,返回函数
hocFunc(func){
let newFunc = ()=>{
let info = "姓名:" + this.data.name + "\n" +
"年龄:" + this.data.age + "\n" +
"性别:" + this.data.sex + "\n" +
"住址:" + this.data.address + "\n" +
"公司:" + this.data.company
func(info)
}
return newFunc
}
let hocWelcome= this.hocFunc(this.welcome)
hocWelcome()
let hocGoodbye = this.hocFunc(this.goodBye)
hocGoodbye()
假如现在增加C页面,就可以这样写
sleep(info){
console.log('sleep:\n',info)
}
let hocSleep = this.hocFunc(this.sleep)
hocSleep()
高阶组件
通过类比,大概可以知道高阶组件是怎么回事喽~
a higher-order component is a function that takes a component and returns a new component.
高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。
将 welcome goodBye 当做普通的组件
import React, {PureComponent, Component} from 'react';
import {
View,
StyleSheet,
Text
} from "react-native"
export default class welcome extends PureComponent {
constructor(props, context){
super(props)
this.state = {
info: ''
}
this.data = {
name: '张三',
age: '20',
sex: '男',
address: '北京通州区',
company: '北京朝阳区'
}
}
componentDidMount() {
let info = "姓名:" + this.data.name + "\n" +
"年龄:" + this.data.age + "\n" +
"性别:" + this.data.sex + "\n" +
"住址:" + this.data.address + "\n" +
"公司:" + this.data.company
this.setState({
info
})
}
render() {
return (
<View style={styles.view}>
<Text style={styles.text}>welcome:{this.state.info}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
view: {
justifyContent: 'center',
alignItems: 'center',
height: 200,
backgroundColor: 'red'
},
text: {
color: 'white',
marginBottom: 30
}
})
import React, {PureComponent, Component} from 'react';
import {
View,
StyleSheet,
Text
} from "react-native"
export default class goodbye extends PureComponent {
constructor(props, context){
super(props)
this.state = {
info: ''
}
this.data = {
name: '张三',
age: '20',
sex: '男',
address: '北京通州区',
company: '北京朝阳区'
}
}
componentDidMount() {
let info = "姓名:" + this.data.name + "\n" +
"年龄:" + this.data.age + "\n" +
"性别:" + this.data.sex + "\n" +
"住址:" + this.data.address + "\n" +
"公司:" + this.data.company
this.setState({
info
})
}
render() {
return (
<View style={styles.view}>
<Text style={styles.text}>goodBye:{this.state.info}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
view: {
justifyContent: 'center',
alignItems: 'center',
height: 200,
backgroundColor: 'blue'
},
text: {
color: 'white',
marginBottom: 30
}
})
设计高阶组件(方式一:属性代理)
属性代理:组件的数据流动都由普通组件流通到高阶组件内部,通过高阶组件再传递给新的组件。高阶组件可以对数据提前进行增删改查等操作,也可以对组件的布局重定义
高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。
最简单的高阶组件
import React, {Component} from 'react'
// 获取组件的名字
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}
export default (WrappedComponent) => {
class NewComponent extends Component {
render() {
return <WrappedComponent {...this.props}/>
}
}
NewComponent.displayName = `HOC(${getDisplayName(WrappedComponent)})`;
return NewComponent
}
优化一下下......
welcome
import React, {PureComponent, Component} from 'react';
import {
View,
StyleSheet,
Text
} from "react-native"
import HocComponent from './hocComponent'
// 方法一:
// ES7 语法,装饰器 export default welcome
@HocComponent
class welcome extends PureComponent {
constructor(props, context){
super(props)
}
render() {
return (
<View style={styles.view}>
<Text style={styles.text}>welcome:{this.props.info}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
view: {
justifyContent: 'center',
alignItems: 'center',
height: 200,
backgroundColor: 'red'
},
text: {
color: 'white',
marginBottom: 30
}
})
export default welcome
// 方法二:
// export default HocComponent(welcome)
goodbye
import React, {PureComponent, Component} from 'react';
import {
View,
StyleSheet,
Text
} from "react-native"
import HocComponent from './hocComponent'
// 方法一:
// ES7 语法,装饰器 export default welcome
@HocComponent
class goodbye extends PureComponent {
constructor(props, context){
super(props)
}
render() {
return (
<View style={styles.view}>
<Text style={styles.text}>goodBye:{this.props.info}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
view: {
justifyContent: 'center',
alignItems: 'center',
height: 200,
backgroundColor: 'blue'
},
text: {
color: 'white',
marginBottom: 30
}
})
export default goodbye
// 方法二:
// export default HocComponent(goodbye)
hocComponent 高阶组件
import React, {Component} from 'react'
import {
View,
Text
} from 'react-native'
export default (WrappedComponent) => {
// WrappedComponent 传进来的组件
class NewComponent extends Component {
// 在 返回新的组件之前,对数据进行处理
constructor(props){
super(props)
this.state = {
info: ''
}
this.data = {
name: '张三',
age: '20',
sex: '男',
address: '北京通州区',
company: '北京朝阳区'
}
this.newProps = {
phone: '151......',
email: '111@...',
qq: '123...',
wx: 'qwer...'
}
}
componentDidMount() {
let info = "姓名:" + this.data.name + "\n" +
"年龄:" + this.data.age + "\n" +
"性别:" + this.data.sex + "\n" +
"住址:" + this.data.address + "\n" +
"公司:" + this.data.company
this.setState({
info
})
}
// {...this.props} 确保原来的组件的 props 不变
// info={this.state.info} 对新的组件进行新的赋值
// {...this.newProps} 对新的组件进行新的传参
// <View>......</View> 对新的组件进行扩展,加入新的组件
render() {
return <View style={{marginTop: 40, justifyContent: 'center', alignItems: 'center', backgroundColor: 'purple'}}>
<Text style={{color: 'white'}}>通过 高阶组件 进行处理后的组件</Text>
<WrappedComponent {...this.props} info={this.state.info} {...this.newProps}/>
</View>
}
}
return NewComponent
}
普通组件还可以向高阶组件传值
import React, {Component} from 'react'
import {
View,
Text
} from 'react-native'
export default (WrappedComponent) => (options) => {
// WrappedComponent 传进来的组件
// options 普通组件传进来的参数
class NewComponent extends Component {
// 在 返回新的组件之前,对数据进行处理
constructor(props){
super(props)
this.state = {
info: ''
}
this.data = {
name: '张三',
age: '20',
sex: '男',
address: '北京通州区',
company: '北京朝阳区'
}
this.newProps = {
phone: '151......',
email: '111@...',
qq: '123...',
wx: 'qwer...'
}
}
componentDidMount() {
let info = "姓名:" + this.data.name + "\n" +
"年龄:" + this.data.age + "\n" +
"性别:" + this.data.sex + "\n" +
"住址:" + this.data.address + "\n" +
"公司:" + this.data.company
this.setState({
info
})
}
// {...this.props} 确保原来的组件的 props 不变
// info={this.state.info} 对新的组件进行新的赋值
// {...this.newProps} 对新的组件进行新的传参
// <View>......</View> 对新的组件进行扩展,加入新的组件
render() {
return <View style={{marginTop: 40, justifyContent: 'center', alignItems: 'center', backgroundColor: 'purple'}}>
<Text style={{color: 'white'}}>通过 高阶组件 进行处理后的组件:{options.title}</Text>
<WrappedComponent {...this.props} info={this.state.info} {...this.newProps}/>
</View>
}
}
return NewComponent
}
ES7语法 装饰器 使用方法
@HocComponent
class welcome extends PureComponent {
}
export default welcome({title: 'A'})
普通的使用方法
export default HocComponent(goodbye)({title: 'B'})
设计高阶组件(方式二:反向继承)
高阶组件继承于普通组件,即 高阶组件可以获取、重写普通组件里面的函数,包括生命周期函数;可以修改普通组件内部state数据
import React, {PureComponent, Component} from 'react';
import {
View,
StyleSheet,
Text
} from "react-native"
import HocComponent from './hoc1Component'
// 方法一:
// ES7 语法,装饰器 export default sleep
@HocComponent
class sleep extends PureComponent {
constructor(props, context){
super(props)
this.state = {
result: '组件'
};
this.actionClick = this.actionClick.bind(this)
}
componentDidMount() {
console.log('2')
}
actionClick(){
return "这是一个 "
}
render() {
console.log('render')
return (
<View style={styles.view}>
<Text style={styles.text}>sleep:{this.state.result}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
view: {
justifyContent: 'center',
alignItems: 'center',
height: 200,
backgroundColor: 'green'
},
text: {
color: 'white',
marginBottom: 30
}
});
export default sleep
// 方法二:
// export default HocComponent(sleep)
高阶组件(反向继承)
import React, {Component} from 'react'
export default (WrappedComponent) => {
// 继承 于 WrappedComponent
class NewComponent extends WrappedComponent {
componentDidMount() {
// 此处 重写了父类的方法,父类就不会再执行 componentDidMount()
console.log('1')
// 修改父类的 state
this.setState({
result: '通过高阶组件(反向继承方式)创建的组件'
})
}
// 重写 父类 的方法
actionClick(){
return "abc"
}
render(){
return super.render()
}
}
return NewComponent
}
不想把整个组件当成高阶组件时,比如组件内部的某些组件需要高阶组件来装饰,可以这样写
function CustomButton() {
return <button ...... />;
}
const MyButton = withTitle(Button);
CustomButton
是自定义的组件,withTitle
是一个高阶组件(比如可以添加防重复点击),MyButton
是在render()
里面要使用的组件
普通组件的 static 方法怎么传入高阶组件内部
当使用高阶组件包装组件,原始组件被容器组件包裹,也就意味着新组件会丢失原始组件的所有静态方法
解决这个问题的方法就是,将原始组件的所有静态方法全部拷贝给新组件:
普通组件内部定义了 static 方法
static ABC(){
return 'abc'
}
高阶组件内部
NewComponent.ABC = WrappedComponent.ABC
注意事项
不要在render函数中使用高阶组件
- 每一次render函数调用都会创建一个新的高阶组件实例 ,引起性能问题
- 会引起原有组件的所有状态和子组件丢失
必须将静态方法做拷贝
- 当使用高阶组件包装组件,原始组件被容器组件包裹,也就意味着新组件会丢失原始组件的所有静态方法。
Refs属性不能传递
更加详细的文档参考RN中文网 https://react.docschina.org/docs/higher-order-components.html