提到高阶组件,不由得想起了函数式编程的高阶函数,高阶函数就是指:接受函数作为输入,或者输出一个函数。例如map,sort等都是高阶函数。
所以高阶函数至少要满足一下一个条件:
1.接受函数作为参数
2.输出一个函数
由此类推,高阶组件就是,接受react组件作为参数,返回一个新的组件。其实它也是一个函数。
先看一个简单的例子
import React from 'react'
import ReactDOM from 'react-dom'
class Wrap extends React.Component{
render(){
return(
<div>
nihao!!
</div>
)
}
}
const newWrap = (Component) => class HOC extends React.Component{
render(){
return(
<div>
<h3>标题</h3>
<Component {...this.props}/>
</div>
)
}
}
const Wrapped = newWrap(Wrap);
ReactDOM.render(
<div>
<Wrapped />
</div>,
document.getElementById('app')
)
结果:
结果我们将Wrap作为参数传入,返回一个新的组件。
如果你学会使用decorator,可以将上述代码换成
import React from 'react'
import ReactDOM from 'react-dom'
const newWrap = (Component) => class HOC extends React.Component{
render(){
return(
<div>
<h3>标题</h3>
<Component {...this.props}/>
</div>
)
}
}
@newWrap
class Wrap extends React.Component{
render(){
return(
<div>
nihao!!
</div>
)
}
}
ReactDOM.render(
<div>
<Wrap />
</div>,
document.getElementById('app')
)
上述代码中,@newWrap其实就是将下面的Wrap为参数传入到newWrap方法中,对于装饰器
function set (target){
target.ss = () => {
console.log(111)
}
}
@set
class A{
}
target就代表了传入的class,@set相当于执行一遍set(A)
如果你想对decorator详细学习请看聊聊es7的decorator修饰器
组件的命名
当包裹新的一个高阶组件,我们便失去了原始的Component的displayName,那么怎么做可以保留原有的名称。
function getDisplayName (component ){
return component .displayName ||
component.name ||
'Component'
}
const newWrap = (Component) => class HOC extends React.Component{
static displayName = `HOC(${getDisplayName(WrappedComponent)})`;
render(){
return(
<div>
<h3>标题</h3>
<Component {...this.props}/>
</div>
)
}
}
实现高阶组件的两种方法
1.属性继承
import React,{Component} from 'react'
const MyConatiner = wrapComponent => class HOC extends Component{
render(){
return <wrapComponent {...this.props}/>
}
}
从以上代码可以看到,我们通过高阶组件来传递props。这种方法叫做属性代理。
我们还可以创建新的props,将被包裹组件的props和新生成的props一起传递给此组件。
import React,{Component} from 'react'
const MyConatiner = wrapComponent => class HOC extends Component{
const newsProps = {
name : this.props.name
}
render(){
return <wrapComponent {...this.props} {...newsProps }/>
}
}
对于原组建来说,我们套用了高阶组件,就得到了name属性。
抽象state
我们可以通过回调函数以及props来抽象state
class Wrap extends React.Component{
render(){
return(
<input type="text" {...this.props}/>
)
}
}
class Home extends React.Component{
constructor(props){
super(props)
this.state = {
name : ''
}
}
onChange(e){
this.setState({
name : e.target.value
})
}
render(){
return (
<div>
<Wrap value={this.state.name} onChange={this.onChange.bind(this)}></Wrap>
</div>
)
}
}
使用高阶组件
const newWrap = (Component) => class WOC extends React.Component{
constructor(props){
super(props)
this.state = {
name : ''
}
this.onChange = this.onChange.bind(this)
}
onChange(e){
this.setState({
name : e.target.value
})
}
render(){
const newsProps = {
value : this.state.name,
onChange : this.onChange
}
return(
<Component {...this.props} {...newsProps}/>
)
}
}
@newWrap
class Wrap extends React.Component{
render(){
return(
<input type="text" {...this.props}/>
)
}
}
2.反向继承
先看例子
const wrapComponent = (wrapComponent) => class wrap extends wrapComponent{
render(){
return super.render()
}
}
高阶组件返回的组件继承了wrapComponent,因为wrapComponent是被继承所以所有的调用都是反向的,因此是反向继承。
这样一来高阶组件可以使用wrapComponent的state、props生命周期和render方法。
渲染劫持
意思就是控制wrapComponent渲染的过程,渲染我们想要的结果。
const newWrap = (wrapComponent) => class WOC extends wrapComponent{
render(){
const element = super.render()
let newsprops = {
value : 'hollow'
};
const props = Object.assign({},element.props,newsprops)
return React.cloneElement(element,props,element.props.children);
}
}
@newWrap
class Wrap extends React.Component{
constructor(props){
super(props)
}
render(){
return(
<input type="text" value="nihao"/>
)
}
}
组件传参
有时我们再调用高阶组件的时候需要传参数,可以:
const newWrap = (title="nihao") => (Component) => class WOC extends React.Component{
constructor(props){
super(props)
this.state = {
name : ''
}
this.onChange = this.onChange.bind(this)
}
onChange(e){
this.setState({
name : e.target.value
})
}
render(){
const newsProps = {
value : this.state.name,
onChange : this.onChange
}
return(
<div>
{title}
<Component {...this.props} {...newsProps}/>
</div>
)
}
}
class Wrap extends React.Component{
render(){
return(
<input type="text" {...this.props}/>
)
}
}
class Home extends React.Component{
constructor(props){
super(props)
this.state = {
name :'',
title : 'hollow'
}
}
render(){
const Wraps = newWrap(this.state.title)(Wrap)
return (
<div>
<Wraps></Wraps>
</div>
)
}
}
那么何时需要使用高阶组件呢?其实高阶组件是一个函数,我们使用高阶组件,目的就是将项目中特定的功能进行封装,使用它装饰组件的时候其实就是给组建增添某项功能。一个组件可以被多个方法装饰,可以添加多个功能。
例如,我们举一个小栗子,封装个给input添加正则的高阶组件:
const newWrap = (params={}) => (Component) => class WOC extends React.Component{
constructor(props){
super(props)
this.state = {
value : '',
placeholder : params.placeholder,
styles : {
'border' : '1px solid #ccc'
}
}
this.blur = this.blur.bind(this)
this.onChange = this.onChange.bind(this)
}
blur(e){
var p = new RegExp(/^\d{11}$/);
if(!e.target.value || !p.test(e.target.value)){
this.setState({
value : '',
placeholder : params.error,
styles : {
'border' : '1px solid red'
}
})
}else{
this.setState({
value : e.target.value,
placeholder : params.placeholder,
styles : {
'border' : '1px solid #ccc'
}
})
}
}
onChange(e){
this.setState({
value : e.target.value
})
}
render(){
const newsProps = {
placeholder : this.state.placeholder,
onChange : this.onChange,
onBlur : this.blur,
value : this.state.value,
style : {...this.state.styles}
}
return(
<Component {...this.props} {...newsProps}/>
)
}
}
@newWrap({
placeholder : '请输入电话号码',
error : '必须为11为数字'
})
class Wrap extends React.Component{
render(){
return(
<input type="text" {...this.props}/>
)
}
}
当然,正则表达式可以作为一个可变参数传入。
网友评论