工作中虽然在用react技术栈做开发,但react hooks 还没有实际用到,一直都在用redux。毕竟都2021了,如果还不会hooks真有点说不过去了,会怀疑我还是前端吗?
所以最近打算学习下 hooks。今天就记录一些浅显的理解,通过具体示例来感受下hooks的魅力。
个人理解
react hooks的作用:实现状态处理逻辑的封装,他是一个全新的API,彻底解决之前类组件中『状态处理逻辑』很难复用的问题,其实和render props
和hoc
的功效是一样的。只不过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 props
和HOC
这两种模式确实达到了状态处理逻辑复用的目的,那有没有更好的方式呢?
是时候表演真正的技术了。------ 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。