HOC、Render props VS Hooks


工作中虽然在用react技术栈做开发,但react hooks 还没有实际用到,一直都在用redux。毕竟都2021了,如果还不会hooks真有点说不过去了,会怀疑我还是前端吗?

所以最近打算学习下 hooks。今天就记录一些浅显的理解,通过具体示例来感受下hooks的魅力。

个人理解

react hooks的作用:实现状态处理逻辑的封装,他是一个全新的API,彻底解决之前类组件中『状态处理逻辑』很难复用的问题,其实和render propshoc的功效是一样的。只不过hooks更彻底,更好用。

感受hooks的魅力

下面通过具体的代码示例来进行分析和理解、感受hooks的作用和魅力。

比如我们现在有个需求要实现两种产品列表,用于前后台展示。

前台产品列表

后台产品列表

常规的,类组件处理方式

看到上面的需求后,常规做法是会分别写两个组件,然后使用统一的产品列表api请求方法获取数据,然后处理状态。

前台产品列表组件

import React from 'react'
//产品列表api获取方法
import { getProductList } from '../../module/product'

export default class FrontProductList extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
      proList: []
    }
  }

  componentDidMount() {
    getProductList().then(list => {
      this.setState({
        proList: list
      })
    })
  }

  render() {
    return <div className="procardlist"><h2>前台产品列表</h2>
      {this.state.proList.map(item => <div key={item.id}>
        <p>产品名称:{item.title}</p>
        <p><img src={item.pic}/></p>
        <p>价格:{item.price}</p>
      </div>)}
    </div>
  }

} 

后台产品列表组件

import React from 'react'
import { getProductList } from '../../module/product'

//产品管理后台list
export default class AdminProductList extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
      proList: []
    }
  }

  componentDidMount() {
    getProductList().then(list => {
      this.setState({
        proList: list
      })
    })
  }

  render() {
    return <div className="procardlist"><h2>管理员看到的产品列表</h2>
      <table>
        <tr>
          <td>产品名称</td>  <td>产品简介</td> <td>价格</td>
        </tr>
        {this.state.proList.map(item => <tr>
          <td>
            {item.title}
          </td>
          <td>
            {item.des}
          </td>
          <td>
            {item.price}
          </td>
        </tr>)}
      </table>
    </div>
  }

} 

我们发现上面两个组件的状态处理逻辑是完全一致的,唯一不同的地方就是render部分。所以我们为了减少重复代码,使状态处理逻辑能够复用,会提取出两个组件无状态list组件,只用于渲染产品列表,且只接受一个props属性-proList(产品列表数据)。

前台产品列表组件

既然是无状态组件,直接使用函数组件即可

// FrontProductList.jsx
import React from 'react'

export default ({proList=[]}) => {
  return <div className="procardlist"><h2>前台产品列表</h2>
    {proList.map(item => <div key={item.id}>
      <p>产品名称:{item.title}</p> <p>价格:{item.price}</p>
    </div>)}
  </div>
}

后台产品列表组件

//AdminProductList.jsx
import React from 'react'
import { getProductList } from '../../module/product'

//产品管理后台list
export default ({proList=[]})=> {
    return <div className="procardlist"><h2>后台产品列表</h2>
      <table>
        <tr>
          <td>产品名称</td>  <td>产品简介</td> <td>价格</td>
        </tr>
        {proList && proList.map(item => <tr>
          <td>
            {item.title}
          </td>
          <td>
            {item.des}
          </td>
        </tr>)}
      </table>
    </div>
} 

那现在我们的状态处理组件就变成下面这样了。

//ProductData.jsx
import React from 'react'
import { getProductList } from '../../module/product'

export default class ProductData extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
      proList: []
    }
  }

  componentDidMount() {
    getProductList().then(list => {
      this.setState({
        proList: list
      })
    })
  }
  //但我应该渲染哪个组件呢
  render() {
    return ?????
  }
}

但上面代码并没有写完,因为我们发现在render方法里我们不知道该渲染哪个列表组件?

使用render props

render props并不是什么新特性,只是一种函数调用模式而已,它可以把特定行为或功能封装成一个组件,提供给其他组件使用让其他组件也拥有这样的能力。而上面的问题就可以通过render props来解决。

说白了就是回调方法。

function GetList(callback){
 const list = getProductList()
    const result = callback(list)
    return reuslt
}

那对应到组件的实现方式想必你已经想到了。

//ProductData.jsx
import React from 'react'
import { getProductList } from '../../module/product'
export default class ProductData extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
      proList: []
    }
  }

  componentDidMount() {
    getProductList().then(list => {
      this.setState({
        proList: list
      })
    })
  }
  //通过回调的方式来拿到要渲染的组件,父组件内部完全不需要知道渲染的是什么组件
  render() {
  return <div>{
    this.props.render(this.state.proList)}</div>
  }
}

调用方式

前台产品列表

<ProductData render={result=><FrontProductList proList={result}/>}/> 

后台产品列表

<ProductData render={result=><AdminProductList proList={result}/>}/> 

使用HOC

对于状态逻辑处理的复用,除了render props能做到,HOC也能做到,只是实现方式不同。

高阶组件的定义就不多说了,直接上代码。

import React from 'react'
import { getProductList } from '../../module/product'

//WrapperdComponent就是我们要渲染的产品列表组件

export default (WrapperdComponent)=>{
//ProductData 就是具有状态处理能力的组件
  return class ProductData extends React.Component {
    constructor(props) {
      super(props)
      this.state = {
        proList: []
      }
    }
  
    componentDidMount() {
      getProductList().then(list => {
        this.setState({
          proList: list
        })
      })
    }
    render() {
    //最终在这里渲染我们的列表组件
     return <WrapperdComponent {...this.props} proList={this.state.proList}/>
    }
  }

}

调用方式

前台产品列表

  ......
  const FrontProductList = ProductData(FrontProductList)
  ......
  render(){
   return <FrontProductList/>
  }

后台产品列表

  ......
  const AdminProductList = ProductData(AdminProductList)
  .....
  render(){
   return <AdminProductList/>
  }

使用hooks

上面我们使用render propsHOC这两种模式确实达到了状态处理逻辑复用的目的,那有没有更好的方式呢?

是时候表演真正的技术了。------ hooks闪亮登场。

先熟悉下,看下基于hooks的常规处理方式

//ProductList.jsx

import React from 'react'
import { getProductList } from '../../module/product'
import {useState,useEffect} from 'react'

export default function ProductList(){
  //获得产品列表数据
  const [proList,setProductList] = useState([])
  useEffect(()=>{
    getProductList().then(list => {
      setProductList(list)
    })
  },[])

  return <div className="procardlist"><h2>管理员看到的产品列表 - hooks</h2>
  <table>
    <tr>
      <td>产品名称</td>  <td>产品简介</td>
    </tr>
    {proList.map(item => <tr key={item.id}>
      <td>
        {item.title}
      </td>
      <td>
        {item.des}
      </td>
    </tr>)}
  </table>
</div>

}

继续我们的菜, 在最开始我们提取出来的两个产品列表组件依然可用。

AdminProductList.jsx
FrontProductList.jsx

对于状态处理逻辑部分的复用方式,可以提取成自定义hooks

//useProductData.jsx

import React from 'react'
import { getProductList } from '../../module/product'
import {useState,useEffect} from 'react'

export default function useProductData(){
  const [proList,setProductList] = useState([])
  useEffect(()=>{
    getProductList().then(list => {
      setProductList(list)
    })
  },[])

  return proList //返回列表数据
}

调用方式

....
import useProductData from './components/ProductListHooks/useProductData'
import AdminProductList from './components/AdminProductList'
import FrontProductList from './components/FrontProductList'


function App(){
  const prolist = useProductData()
     return <AdminProductList proList={prolist}/>
}

写到这里,不得不说hooks是真的香。

之前的逻辑复用只能是通过组件的方式来复用,代码多,上手成本也高。

而hooks直接突破,可以将状态处理逻辑直接封装成函数来进行调用。

小结

本文内容比较浅,所以咱们就浅显的看,其实react hooks就是几个api,这几个api调用都非常简单(只谈使用,原理后面再说),但可以实现完全使用函数组件达到原来类组件的效果,而且代码量少,不需要关心this,没有生命周期,最主要的是可以将状态处理逻辑封装成高度复用的函数,这是突破也是颠覆,更承认这是react的未来。

也许你已经习惯了类组件的开发方式,但就仅仅这种颠覆的逻辑复用方式,hooks就值得一试。

后面会用更多的代码示例来学习react hooks。

点个『在看』支持下 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zz_jesse

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值