Mixin
在了解高阶组件
之前我们先讲一下mixin
很多初级前端工程师对mixin的概念并不是很了解,首先解释一下mixin
mixin
:在项目中有些一代码相同的代码会重复使用,我们就可以进行抽离,方便维护,为了解决这个问题就是包装成mixin方法,但是后来发现有mixin有很多弊端,也许可以说高阶组件就是mixin的衍生品,让我们进入今天的主题
高阶组件
高阶组件
:本身是一个函数,这个函数接受一个组件
做为参数,并且返回一个组件
实现方式:属性代理
和反向继承
示例:
function Hoc(WrappedComponent) {//hoc就是是个高阶组件 接受一个组件做为参数
return class extends Component {//返回一个组件
render() {
return (
这是一个高阶组件
)
}
}
}
那么高阶组件
有什么用?
- 代码复用
- 抽离state
- 操作props
- 获取实例
- 布局更改
接下来我们一个个例子查看它的好处
现在有一个组件ConfirmBox
点击按钮后消失
class Dialog extends Component {
constructor(props) {
super(props);
this.state = {
show:true,
msg:'这是一个对话框',
title:'对话框'
}
}
handleConfirm = () => {
this.setState({
show:!this.state.show
})
}
render() {
return (
{this.state.title}
{this.state.msg}
)
}
}
属性代理
现在我们使用高阶组件
把ConfirmBox
组件里的state
和回调函数
抽离出来
属性代理可以做什么?
- 抽离state
- 操作props
- 获取实例
- 布局更改
接下来对props
进行操作
function Hoc(WrappedComponent) {//hoc就是是个高阶组件 接受一个组件做为参数
return class extends Component {//返回一个组件
constructor(props) {
super(props);
this.state = {
show:true,
msg:'这是一个对话框',
title:'对话框'
}
}
handleConfirm = () => {
this.setState({
show:!this.state.show
})
}
render() {
return (
这是一个高阶组件
)
}
}
}
class ConfirmBox extends Component {
constructor(props) {
super(props);
}
render() {
return (
{this.props.title}
{this.props.msg}
)
}
}
现在我们已经把state
和回调函数
已经抽离到高阶组件中
function Hoc(WrappedComponent) {//hoc就是是个高阶组件 接受一个组件做为参数
return class extends Component {//返回一个组件
constructor(props) {
super(props);
this.state = {
show:true,
msg:'这是一个对话框',
title:'对话框'
}
}
handleConfirm = () => {
this.setState({
show:!this.state.show
})
}
render() {
const newProps = {
show:true,
msg:'这是新的props'
}
return (
这是一个高阶组件
)
}
}
}
class ConfirmBox extends Component {
constructor(props) {
super(props);
}
render() {
return (
{this.props.title}
{this.props.msg}
)
}
}
到这里不知道会不会有人感觉这个和React的UI和逻辑抽离很像,其实是没错的,UI和业务逻辑拆分也类似,那为什么还要有高阶组件呢
这里需要强调一下高阶组件是函数
意味着参数是动态
的这一点很重要,如果有两个组件有同样的逻辑通过高阶组件
只需要改变实参就可以实现而UI和业务逻辑拆分就不行。
假设:现在有一个ConfirmBox2和ComfirmBox1的逻辑一样那我们直接改变参数就可以了
Hoc(ConfirmBox2)
简单一行我们就把逻辑赋予给了ComfirmBox2
使用高阶组件
相对比较灵活,这里我可能找的例子不好,请各位读者见谅。
通过ref访问组件实例
this.myInstance = ref}>
这样我们就得到了组件实习了
反向继承(不推荐使用)
反向继承
:既然有继承两字肯定和extends
脱不了干系,反向继承就是通过继承传进来的组件参数
并且在render
调用super.render
实现的
反向继承
:可以做什么?
- 操作state
- 渲染劫持
最简单例子:
function Hoc(WrappedComponent) {//hoc就是是个高阶组件 接受一个组件做为参数
return class extends WrappedComponent {//继承传进来的组件
render() {
return (
super.render();//调用super.render
)
}
}
}
操作state
:根据state的show
判断是否显示组件
function Hoc(WrappedComponent) {//hoc就是是个高阶组件 接受一个组件做为参数
return class extends WrappedComponent {//返回一个组件
static displayName = `HOC(${WrappedComponent.displayName || WrappedComponent.name})`
componentWillMount() {
// 可以方便地得到state,做一些更深入的修改。
this.setState({
msg: '这是hoc修改的值'
});
}
render() {
return (
{ super.render()}
)
}
}
}
渲染劫持
function Hoc(WrappedComponent) {//hoc就是是个高阶组件 接受一个组件做为参数
return class extends WrappedComponent {//返回一个组件
static displayName = `HOC(${WrappedComponent.displayName || WrappedComponent.name})`
componentWillMount() {
// 可以方便地得到state,做一些更深入的修改。
this.setState({
msg: '这是hoc修改的值'
});
}
render() {
if(this.state.show) {
return super.render()
}else {
return null
}
}
}
}
反向代理
:为什么不推荐使用,因为操作state容易覆盖,并且生命周期函数也会覆盖
function Hoc(WrappedComponent) {//hoc就是是个高阶组件 接受一个组件做为参数
return class extends WrappedComponent {//返回一个组件
static displayName = `HOC(${WrappedComponent.displayName || WrappedComponent.name})`
componentWillMount() {
// 可以方便地得到state,做一些更深入的修改。
this.setState({
msg: '这是hoc修改的值' //修改了msg的值
});
}
componentDidMount(){
console.log("hoccomponentDidMount")
}
render() {
if(this.state.show) {
return super.render()
}else {
return null
}
}
}
}
class ConfirmBox extends Component {
constructor(props) {
super(props);
this.state = {
show:true,
msg:'这是一个对话框',
title:'对话框'
}
}
handleConfirm = () => {
this.setState({
show:!this.state.show
})
}
componentDidMount() {
console.log("comfirmbox componentDidMount")
}
render() {
return (
{this.state.title}
{this.state.msg}
)
}
}
[图片上传失败...(image-83c9fd-1599201044014)]
从图片可以看出被包裹组件
的componentDidMount
生命周期函数组覆盖了,如果要调用需要使用super.componentDidMount
去调用,而且state也被覆盖了
另外最重要一点反向继承
不能保证子组件完全被渲染,这是什么意思?就是被包裹组件
里的可能会丢失
高阶组件使用场景
页面渲染
function Hoc(WrappedComponent) {//hoc就是是个高阶组件 接受一个组件做为参数
return class Test extends Component {//返回一个组件
static displayName = `HOC(${WrappedComponent.displayName || WrappedComponent.name})`
constructor(props) {
super(props);
this.state = {
show:true,
msg:'这是一个对话框',
title:'对话框'
}
}
handleConfirm = () => {
this.setState({
show:!this.state.show
})
}
componentDidMount() {
console.log(this.myInstance)
}
render() {
const newProps = {
show:true,
msg:'这是新的props'
}
if(this.state.show) {
return this.myInstance = ref}>
}else {
return null
}
}
}
}
逻辑复用
:现在假设有两个确认框组件,他们的逻辑一样,只有样式不同,那我们就可以使用高阶组件
来实现
import React,{Component} from 'react'
import './Hoc.css'
function Hoc(WrappedComponent) {//hoc就是是个高阶组件 接受一个组件做为参数
return class Test extends Component {//返回一个组件
static displayName = `HOC(${WrappedComponent.displayName || WrappedComponent.name})`
constructor(props) {
super(props);
this.state = {
show:true,
msg:'这是一个对话框',
title:'对话框'
}
}
handleConfirm = () => {
this.setState({
show:!this.state.show
})
}
componentDidMount() {
console.log(this.myInstance)
}
render() {
const newProps = {
show:true,
msg:'这是新的props'
}
if(this.state.show) {
return this.myInstance = ref}>
}else {
return null
}
}
}
}
class ConfirmBox extends Component {
constructor(props) {
super(props);
}
render() {
return (
{this.props.title}
{this.props.msg}
)
}
}
class ConfirmBox2 extends Component {
constructor(props) {
super(props);
}
render() {
return (
{this.props.title}
{this.props.msg}
)
}
}
export default {
comfirmBox:Hoc(ConfirmBox),
comfirmBox2:Hoc(ConfirmBox2)
}
可以看出组件只是样式不同,逻辑相同我们就可以使用
高阶组件
来实现逻辑复用