react 的高阶组件

高阶组件的概念

高阶组件就是接受一个组件作为参数并返回一个新组件的函数(也就是说高阶组件是一个函数,并不是组件)

 

 

高阶组件的实现

先来看看要做的案例的效果是怎么样的,下图效果中

第一步:对组件进行拆分首先是外层相同的框为A组件,里面内容分别为B组件和C组件

第二步:代码实现,首先搭建项目,然后分别创建A,B,C三个普通的组件

import React, { Component } from 'react'

export default class A extends Component {
    render() {
        return (
            <div>这是A组件</div>
        )
    }
}
import React, { Component } from 'react'

export default class B extends Component {
    render() {
        return (
            <div><img src={require('../images/Jietu2.jpg')} alt="" /></div>
        )
    }
}
import React, { Component } from 'react'

export default class C extends Component {
    render() {
        return (
            <div><img src={require('../images/Jietu1.jpg')} alt="" /></div>
        )
    }
}

第三步:然后将这三个组件在App.js中引入

import React from 'react';
import './App.css';

import A from './components/A'
import B from './components/B'
import C from './components/C'
function App() {
  return (
    <div className="App">
      <A />
      <B />
      <C />
    </div>
  );
}

export default App;

创建高阶组件:A组件是B组件和C组件共用的一个组件,先来实现A组件。首先定义一个函数(这个函数接受一个参数(组件)),且返回一个组件

import React, { Component } from 'react'

function A(WrappedComponent) {
    return class A extends Component {
        render() {
            return (
                <div className='a-container'>
                    <div className='header'>
                        <div>提示</div>
                        <div>X</div>
                    </div>
                    <div>
                        <WrappedComponent />
                    </div>
                </div>
            )
        }
    }
}
export default A

调用高阶组件:然后在B组件中进行调用这个A组件函数

import React, { Component } from 'react'

import A from './A'
class B extends Component {
    render() {
        return (
            <div><img src={require('../images/Jietu2.jpg')} alt="" /></div>
        )
    }
}

export default A(B)

调用高阶组件:在C组件中进行调用这个A组件函数,使用装饰器方式进行调用,步骤如下:

第一步:运行package.json运行eject脚本,暴露输出配置项

第二步:安装依赖

yarn add babel-preset-stage-2

yarn add babel-preset-react-native-stage-0

npm install babel-preset-react-native-stage-0

第三步:根目录新建.babelrc文件

{
  "presets":["react-native-stage-0/decorator-support"]
}

第四步:export default A(C)改成C,并直接在class上写上@A注解

import React, { Component } from 'react'

import A from './A'

@A
class C extends Component {
    render() {
        return (
            <div><img src={require('../images/Jietu1.jpg')} alt="" /></div>
        )
    }
}

export default C

有种可能,高阶组件A中的结构是一样的,但内容有可能有变化,这时候被包裹组件可以传递参数给高阶组件

首先修改高阶组件A,将原来的函数在封装一层,并且定义参数

import React, { Component } from 'react'

export default (title) => WrappedComponent => class A extends Component {
        render() {
            return (
                <div className='a-container'>
                    <div className='header'>
                        <div>{title}</div>
                        <div>X</div>
                    </div>
                    <div>
                        <WrappedComponent />
                    </div>
                </div>
            )
        }
    }

在被包裹的组件中进行调用这个高阶组件(其实就是函数)

import React, { Component } from 'react'
import A from './A'

class B extends Component {
    render() {
        return (
            <div>
                <img src={require('../images/Jietu2.jpg')} alt="" />
            </div>
        )
    }
}

export default A('提示')(B)
import React, { Component } from 'react'

import A from './A'
class C extends Component {
    render() {
        return (
            <div><img src={require('../images/Jietu1.jpg')} alt="" /></div>
        )
    }
}

export default A('警告')(C)

 

 

 

高阶组件的应用—代理方式

返回的新组件类直接继承自React.Component类,新组件扮演的角色传入参数组件的一个代理,在新组件的render函数中,将被包裹组件渲染出来,除了高阶组件自己要做的工作,其余功能全部转手给了被包裹的组件;主要应用有:操作Props,访问ref,抽取状态,包装组件

操作Props

在App.js中引入B组件并且传递props参数

import React from 'react';
import './App.css';

import A from './components/A'
import B from './components/B'
import C from './components/C'

function App() {
  return (
    <div className="App">
      <A />
      <B name={'张三'} age={18} />
      <C />
    </div>
  );
}

export default App;

在高阶组件A中

import React, { Component } from 'react'

function A(WrappedComponent) {
    return class A extends Component {
        render() {
            return (
                <div className='a-container'>
                    <div className='header'>
                        <div>提示</div>
                        <div>X</div>
                    </div>
                    <div>
                        <WrappedComponent {...this.props} />
                    </div>
                </div>
            )
        }
    }
}
export default A

最后在B组件中使用传递过来的props

import React, { Component } from 'react'
import A from './A'

class B extends Component {
    render() {
        return (
            <div>
                我的名字叫:{this.props.name}
                <br />
                我的年龄是:{this.props.age}
                <br />
                <img src={require('../images/Jietu2.jpg')} alt="" />
            </div>
        )
    }
}

export default A(B)

通过高阶组件对B组件的props属性进行修改(也就是通过高阶组件给被包裹的组件B添加props属性)

import React, { Component } from 'react'

function A(WrappedComponent) {
    return class A extends Component {
        render() {
            return (
                <div className='a-container'>
                    <div className='header'>
                        <div>提示</div>
                        <div>X</div>
                    </div>
                    <div>
                        <WrappedComponent sex={'男'} {...this.props} />
                    </div>
                </div>
            )
        }
    }
}
export default A
import React, { Component } from 'react'
import A from './A'

class B extends Component {
    render() {
        return (
            <div>
                我的名字叫:{this.props.name}
                <br />
                我的年龄是:{this.props.age}
                <br />
                我的性别是:{this.props.sex}
                <br />
                <img src={require('../images/Jietu2.jpg')} alt="" />
            </div>
        )
    }
}

export default A(B)

那么通过高阶组件对B组件中的props属性进行删除

import React, { Component } from 'react'

function A(WrappedComponent) {
    return class A extends Component {
        render() {
            const {age, ...otherProps} = this.props
            return (
                <div className='a-container'>
                    <div className='header'>
                        <div>提示</div>
                        <div>X</div>
                    </div>
                    <div>
                        <WrappedComponent sex={'男'} {...otherProps} />
                    </div>
                </div>
            )
        }
    }
}
export default A
import React, { Component } from 'react'
import A from './A'

class B extends Component {
    render() {
        return (
            <div>
                我的名字叫:{this.props.name}
                <br />
                我的年龄是:{this.props.age}
                <br />
                我的性别是:{this.props.sex}
                <br />
                <img src={require('../images/Jietu2.jpg')} alt="" />
            </div>
        )
    }
}

export default A(B)

访问ref(最好不要用,容易出问题)

在C组件中定义一个方法,在高阶组件A中能调用到,首先在C组件中定义一个方法

import React, { Component } from 'react'

import A from './A'
class C extends Component {
    getName() {
        return '我是C组件'
    }
    render() {
        return (
            <div><img src={require('../images/Jietu1.jpg')} alt="" /></div>
        )
    }
}

export default A(C)

在高阶组件A中进行调用,调用的时候注意进行判空处理

import React, { Component } from 'react'

function A(WrappedComponent) {
    return class A extends Component {
        refc(instance) { //instance就是WrappedComponent的实例
            if (instance.getName) {
                console.log(instance.getName())
            }
        }
        render() {
            const {age, ...otherProps} = this.props
            return (
                <div className='a-container'>
                    <div className='header'>
                        <div>提示</div>
                        <div>X</div>
                    </div>
                    <div>
                        <WrappedComponent sex={'男'} {...otherProps} ref={this.refc.bind(this)} />
                    </div>
                </div>
            )
        }
    }
}
export default A

抽取状态

比如在B组件中有一个input输入框,那么要获取输入框的内容(受控组件),如下

import React, { Component } from 'react'
import A from './A'

class B extends Component {
    constructor(props){
        super(props)
        this.state = {
            value: ''
        }
    }
    changeInput = (event) => {
        const value = event.target.value
        this.setState({
            value
        })
    }

    render() {
        return (
            <div>
                <input type='text' value={this.state.value} onInput={this.changeInput} />
                <br />
                我的名字叫:{this.props.name}
                <br />
                我的年龄是:{this.props.age}
                <br />
                我的性别是:{this.props.sex}
                <br />
                <img src={require('../images/Jietu2.jpg')} alt="" />
            </div>
        )
    }
}

export default A('提示')(B)

但是如果有很多像B组件这样的,那么每个组件都要写,这时候可以使用高阶组件传进来,B组件的input中只要接收Props即可

import React, { Component } from 'react'
import A from './A'

class B extends Component {
    render() {
        return (
            <div>
                <input type='text' {...this.props} />
                <br />
                我的名字叫:{this.props.name}
                <br />
                我的年龄是:{this.props.age}
                <br />
                我的性别是:{this.props.sex}
                <br />
                <img src={require('../images/Jietu2.jpg')} alt="" />
            </div>
        )
    }
}

export default A('提示')(B)

在高阶组件中定义一个props并且传给包裹组件

import React, { Component } from 'react'

export default (title) => WrappedComponent => class A extends Component {
    constructor(props){
        super(props)
        this.state = {
            value: ''
        }
    }

    onInputChange = (event) => {
        const value = event.target.value
        this.setState({
            value
        })
    }

    render() {
        const {age, ...otherProps} = this.props
        const newProps = {
            value: this.state.value,
            onInput: this.onInputChange
        }
        return (
            <div className='a-container'>
                <div className='header'>
                    <div>{title}</div>
                    <div>X</div>
                </div>
                <div>
                    <WrappedComponent sex={'男'} {...otherProps} {...newProps} />
                </div>
            </div>
        )
    }
}

包装组件

以上列子中就是包装组件,包装组件的好处就是可以将所有被包裹的组件公共的地方抽取出来,代码也会比较优雅

 

 

高阶组件的应用—反向继承方式

采用继承关联作为参数的组件和返回的组件,假如传入的组件参数是WrappedComponent,那么返回的组件就是直接继承自WrappedComponent,主要应用有:操作Props,操作生命周期函数

操作Props

操作生命周期函数

高阶组件使用出现的问题

 

 

高阶组件案例—实现一个tabbar

页面效果

创建项目并且实现静态布局

首先使用脚手架创建项目,并且将图片的字体图标资源拷贝到项目中(直接在阿里字体图标库找几个图标),在App.js中引入字体图标库

然后创建一个文件夹:component,创建tabbar文件夹(index.js和index.css),实现静态的布局。样式文件直接拷贝下面:

.iconfont {
    font-size: 28px !important;
}

.tabbar-children{
    margin-bottom: 50px;
}

.tabbar {
    background: #ffffff;
    height: 50px;
    position: fixed;
    bottom: 0;
    width: 100%;
    border: 1px solid rgba(204, 204, 204, 0.43);
    padding: 5px 0;
    text-align: center;
}

.tabbar-content {
    display: flex;
}

.tarbar-item {
    flex: 1;
}

.active {
    color: red !important;
}
import React, {Component} from 'react';
// import { Link } from 'react-router-dom';
import './index.css'

const tarbarArr = [
    {
        img: 'icon-home',
        text: '首页',
        link: '/home'
    },
    {
        img: 'icon-fenlei',
        text: '分类',
        link: '/category'
    },
    {
        img: 'icon-gouwuche',
        text: '购物车',
        link: '/car'
    },
    {
        img: 'icon-msnui-user',
        text: '我的',
        link: '/user'
    },
]


class Index extends Component {
    render() {
        return (
            <div className='tabbar'>
                <div className='tabbar-content'>
                    {
                        tarbarArr.map((item, index) => (
                            <div key={index} className='tarbar-item'>
                                <div className={'iconfont ' + item.img}></div>
                                <div>{item.text}</div>
                            </div>
                        ))
                    }
                </div>
            </div>
        );
    }
}

export default Index;

效果如下:

给每个tabbar添加点击事件

import React, {Component} from 'react';
// import { Link } from 'react-router-dom';
import './index.css'

const tarbarArr = [
    {
        img: 'icon-home',
        text: '首页',
        link: '/home'
    },
    {
        img: 'icon-fenlei',
        text: '分类',
        link: '/category'
    },
    {
        img: 'icon-gouwuche',
        text: '购物车',
        link: '/car'
    },
    {
        img: 'icon-msnui-user',
        text: '我的',
        link: '/user'
    },
]


class Index extends Component {
    constructor(props) {
        super(props)
        this.state = {
            index: 0
        }
    }
    itemChange = (index) => {
        this.setState({
            index
        })
    }
    render() {
        return (
            <div className='tabbar'>
                <div className='tabbar-content'>
                    {
                        tarbarArr.map((item, index) => (
                            <div key={index} className={'tarbar-item ' + (this.state.index === index ? 'active' : '')} onClick={() => this.itemChange(index)}>
                                <div className={'iconfont ' + item.img}></div>
                                <div>{item.text}</div>
                            </div>
                        ))
                    }
                </div>
            </div>
        );
    }
}

export default Index;

创建每个tabbar对应的页面

import React, { Component } from 'react';

class Home extends Component {
  render() {
    return (
      <div>
        <img className='bg' src={require('../static/images/home.png')} alt="" />
      </div>
    );
  }
}

export default Home;
import React, { Component } from 'react';

class Category extends Component {
  render() {
    return (
      <div>
        <img className='bg' src={require('../static/images/category.png')} alt="" />
      </div>
    );
  }
}

export default Category;
import React, { Component } from 'react';

class Car extends Component {
  render() {
    return (
      <div>
        <img className='bg' src={require('../static/images/car.png')} alt="" />
      </div>
    );
  }
}

export default Car;
import React, { Component } from 'react';

class User extends Component {
  render() {
    return (
      <div>
        <img className='bg' src={require('../static/images/user.png')} alt="" />
      </div>
    );
  }
}

export default User;

src下创建路由文件router.js,并且在App.js中引入

import React from 'react'
import { BrowserRouter, Route, Switch } from 'react-router-dom'

import Home from './pages/home'
import Category from './pages/category'
import Car from './pages/car'
import User from './pages/user'

export default () => (
    <BrowserRouter>
        <Switch>
            <Route path='/' exact component={Home} />
            <Route path='/home' component={Home} />
            <Route path='/category' component={Category} />
            <Route path='/car' component={Car} />
            <Route path='/user' component={User} />
        </Switch>
    </BrowserRouter>
)
import React from 'react';
import RouterMap from './router';
import './static/iconfont.css';
import './App.css';

function App() {
  return (
    <div className="App">
      <RouterMap />
    </div>
  );
}

export default App;

将tabbar组件改造成高阶组件并且在每个页面进行调用

import React, {Component} from 'react';
// import { Link } from 'react-router-dom';
import './index.css'

const tarbarArr = [
    {
        img: 'icon-home',
        text: '首页',
        link: '/home'
    },
    {
        img: 'icon-fenlei',
        text: '分类',
        link: '/category'
    },
    {
        img: 'icon-gouwuche',
        text: '购物车',
        link: '/car'
    },
    {
        img: 'icon-msnui-user',
        text: '我的',
        link: '/user'
    },
]

const Tabbar = (WrappedComponent) => class Tabbar extends Component{
    constructor(props) {
        super(props)
        this.state = {
            index: 0
        }
    }
    itemChange = (index) => {
        this.setState({
            index
        })
    }
    render() {
        return (
            <div className='tabbar-container'>
                <div className='tabbar-children'>
                    <WrappedComponent />
                </div>
                <div className='tabbar'>
                    <div className='tabbar-content'>
                        {
                            tarbarArr.map((item, index) => (
                                <div key={index} className={'tarbar-item ' + (this.state.index === index ? 'active' : '')} onClick={() => this.itemChange(index)}>
                                    <div className={'iconfont ' + item.img}></div>
                                    <div>{item.text}</div>
                                </div>
                            ))
                        }
                    </div>
                </div>
            </div>
        );
    }
}

export default Tabbar;
import React, { Component } from 'react';
import Tabbar from '../components/tabbar'

class Home extends Component {
  render() {
    return (
      <div>
        <img className='bg' src={require('../static/images/home.png')} alt="" />
      </div>
    );
  }
}

export default Tabbar(Home);
import React, { Component } from 'react';
import Tabbar from '../components/tabbar';

class Car extends Component {
  render() {
    return (
      <div>
        <img className='bg' src={require('../static/images/car.png')} alt="" />
      </div>
    );
  }
}

export default Tabbar(Car);
import React, { Component } from 'react';
import Tabbar from '../components/tabbar';

class User extends Component {
  render() {
    return (
      <div>
        <img className='bg' src={require('../static/images/user.png')} alt="" />
      </div>
    );
  }
}

export default Tabbar(User);
import React, { Component } from 'react';
import Tabbar from '../components/tabbar';

class Category extends Component {
  render() {
    return (
      <div>
        <img className='bg' src={require('../static/images/category.png')} alt="" />
      </div>
    );
  }
}

export default Tabbar(Category);

给每个tabbar添加路由和路由切换

import React, {Component} from 'react';
import { Link } from 'react-router-dom';
import './index.css'

const tarbarArr = [
    {
        img: 'icon-home',
        text: '首页',
        link: '/home'
    },
    {
        img: 'icon-fenlei',
        text: '分类',
        link: '/category'
    },
    {
        img: 'icon-gouwuche',
        text: '购物车',
        link: '/car'
    },
    {
        img: 'icon-msnui-user',
        text: '我的',
        link: '/user'
    },
]

const Tabbar = (WrappedComponent) => class Tabbar extends Component{
    constructor(props) {
        super(props)
        this.state = {
            index: 0
        }
    }
    itemChange = (index) => {
        this.setState({
            index
        })
    }
    render() {
        const url = window.location.href
        return (
            <div className='tabbar-container'>
                <div className='tabbar-children'>
                    <WrappedComponent />
                </div>
                <div className='tabbar'>
                    <div className='tabbar-content'>
                        {
                            tarbarArr.map((item, index) => (
                                <Link to={item.link} key={index} className={'tarbar-item ' + (url.indexOf(item.link) > -1 ? 'active' : '')} onClick={() => this.itemChange(index)}>
                                    <div className={'iconfont ' + item.img}></div>
                                    <div>{item.text}</div>
                                </Link>
                            ))
                        }
                    </div>
                </div>
            </div>
        );
    }
}

export default Tabbar;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值