【AICon】 如何构建高效的 RAG 系统?RAG 技术在实际应用中遇到的挑战及应对策略?>>> 了解详情
写点什么

“混合双打”之如何在 Class Components 中使用 React Hooks

  • 2021-07-31
  • 本文字数:4187 字

    阅读完需:约 14 分钟

“混合双打”之如何在 Class Components 中使用 React Hooks

前情提要


React 在 v16.8.0 版本中推出了 Hook,作为纯函数组件的增强,给函数组件带来了状态、上下文等等;之前一篇关于 React Hooks 的文章介绍了如何使用一些官方钩子和如何自建钩子,如果想要了解这些内容的同学可以访问《看完这篇,你也能把 React Hooks 玩出花》。


本文不会再介绍上文中已提到的部分钩子的基础使用,而是主要着眼解决一些实际开发中的场景。


现状


Class Component 内部复杂的生命周期函数使得我们组件内部的 componentDidMount 越来越复杂和臃肿,独立组件动辄上千行代码;组件嵌套层级越来越深,组件之间的状态复用也变得非常困难。


Hook 无疑是可选的,他不会对现有项目造成任何冲击和破坏,社区对于它的优势也有过很多讨论;不过目前官方也没有计划移除 Class,而是推荐渐进式的去使用 Hook,在一些新增的组件中优先选用 Hook。那么我们想要在原有以 Class Component 为主的项目中开始使用 Hook,与原有的 Class Component 必然会产生交互,是不是需要将这些 Class Component 重写为 Hook 呢?


将部分复杂的 Class Component 逐步重写为 Hook 应该排在项目迭代的中长期计划中,如果想要在一个迭代中进行大量改造,带来的巨大成本和副作用也是无法估量的。


那么短期内我们就绕不开 Hook 与 Class 组件的混合使用。


解决方案


先简单介绍一下两种组件的基本写法:


Class Components:类组件的写法


export default class ShowHook extends Component { return (    <h1>Hello Hook!</h1> );}
复制代码


Function Components:Hook 组件的写法


function ShowHook (props){ return (    <h1>Hello Hook!</h1> );}
复制代码


混合使用就难以避免的需要进行通信和参数传递,下面我用一个简单的处理模块显示隐藏的功能组件 ShowHook 作为一个例子,介绍三种是比较常见混合使用的方式,先来看一下效果:



1.Render props


Render props 中来自父组件的 props children 是一个 Function,我们可以将子组件的内部变量通过函数传递至父组件,达到通信的目的。


// 子组件 SayHello.jsimport React, { useState } from 'react';function sayHello({ children }) {  const [visible, changeVisible] = useState(false);   const jsx = visible && (    <h1 onClick={() => changeVisible(false)}> Hello Hook! </h1>  );  return children({ changeVisible, jsx });}export default sayHello;
复制代码


父组件获取到 changeVisible 方法之后就能方便的控制 visible 的状态。


// 父组件 ShowHook.jsimport React, { Component, Fragment } from 'react';import SayHello from '../components/SayHello';export default class ShowHook extends Component {  render() {    return (      <SayHello>        {({ changeVisible, jsx }) => {          return (            <>              <button onClick={() => changeVisible(true)}>                showChild              </button>              {jsx}            </>          );        }}      </SayHello>    );  }}
复制代码


props.children 常用的类型是字符串、对象甚至数组;但其实我们也可以传入一个函数,只要最终能返回出 DOM 树即可;Render props 是将 Render 部分抽离出来作为函数传入子组件;它主要的作用是将 state 部分抽成组件,实现 state 的复用。



// 封装子组件function Mouse (props) {  const [position, setPosition] = useState({x: 0,y: 0});  const handleMouseMove = (e) => {    setPosition({ x: e.clientX, y: e.clientY })  }  return (    <div onMouseMove={handleMouseMove}>        {this.props.children(position)}    </div>  )}// 使用场景 1:图片位置跟随鼠标class Cat1 extends React.Component {  render() {    return (      <Mouse>        {(position) =>           <img src="/cat.jpg"             style={{ position: 'absolute', left: position.x, top: position.y }}           />        }      </Mouse>    )  }}// 使用场景 2:页面展示鼠标坐标class Cat2 extends React.Component {  render() {    return (      <Mouse>        {(position) =>           <h1>x: {position.x} y: {position.y}</h1>        }      </Mouse>    )  }}
复制代码


上面使用了 React 官方文档中的例子进行改写,具体效果如下:


场景 1:

场景 2:


2.使用 HOC


HOC (Higher-Order Components) 是另一种提高代码复用率的常见技巧,它接收一个组件作为参数,最终返回出一个新的组件。


下面的方法使得 button 控制任意组件显示隐藏的功能被封装为高阶组件,得以复用,并且 setVisible 方法也能被传递到 Class Component 中。


// 高阶组件 SayHello.jsimport React, { useState, Fragment } from 'react';const sayHello = (Component) => {  return (props) => {    const [visible, setVisible] = useState(false);    return (      <Fragment>        <button onClick={() => setVisible(true)}>          showChild        </button>        {visible && <Component changeVisible={setVisible} visible={visible} />}      </Fragment>    );  };};export default sayHello;
复制代码


在外部 Class Component 中我们可以定制受内部显示/隐藏控制的组件,并且使用高阶组件中向外传递的 props。


// ShowHook.jsimport React, { Component } from 'react';import SayHello from '../components/SayHello';class ShowHook extends Component {render() {  const { changeVisible } = this.props;  return (    <h1 onClick={() => changeVisible(false)}> Hello Hook! </h1>  );}}export default SayHello(ShowHook);
复制代码


HOC 在实际使用中是将一些副作用函数、公用方法作为组件抽取出来,从而提升复用率;我们可以把父组件的render 部分改为一个弹窗,或任意内容使得子组件得到复用,例如:


// 复用高阶组件 SayHelloimport React, { Component } from 'react';import SayHello from '../components/SayHello';import { Modal } from 'antd';class ShowModal extends Component {  render() {    const { changeVisible, visible } = this.props;    return (      <Modal          title="Basic Modal"          visible={visible}        onOk={() => changeVisible(false)}          onCancel={() => changeVisible(false)}      >          <p>Some contents...</p>          <p>Some contents...</p>          <p>Some contents...</p>      </Modal>    ); }}export default SayHello(ShowHook);
复制代码


这样就可以轻松的控制弹窗的显示隐藏;实际效果如下:



3.useImperativeHandle & Refs 转发 (React.forwardRef)

Ref 转发是一项将 Ref (https://zh-hans.reactjs.org/docs/refs-and-the-dom.html) 自动地通过组件传递到其一子组件的技巧。对于大多数应用中的组件来说,这通常不是必需的,但其对某些组件,尤其是可重用的组件库是很有用的。


它可以将子组件的方法暴露给父组件使用。


// 父组件 ShowHook.jsimport React, { Component } from 'react';import SayHello from './SayHello';export default class ShowHook extends Component {  showChild = () => {    console.log(this.child);    //可以看到 changeVisible 方法被挂载到了 this.child 下    // {changeVisible: f()}    this.child.changeVisible(true);  }  // 将子组件暴露出来的对象挂载到 child  onRef = (ref) => {    this.child = ref;  }  render()  {    return (      <>          <button onClick={this.showChild}>showChild</butotn>        <SayHello          ref={this.onRef}        />      </>    );  }}// 子组件 SayHello.jsimport React, { useState, useImperativeHandle, forwardRef } from 'react';function SayHello(props, ref) {  const [visible, changeVisible] = useState(false);   // 暴露的子组件方法,给父组件调用  useImperativeHandle(ref, () => {    return {      changeVisible,    };  });  return visible && (    <h1 onClick={() => changeVisible(false)}> Hello Hook! </h1>  );}export default forwardRef(SayHello);
复制代码


上面例子中封装了一个子组件,任意一个使用了该子组件的地方都可以控制它的状态。


结束语


目前 Hooks 尚不具备完整的 Class Component 的功能,一些不常用的生命周期函数尚不支持,例如:getSnapshotBeforeUpdategetDerivedStateFromError 以及 componentDidCatch,但官方已将他们 排入计划内,相信不久之后就会得到支持;未来 Hooks 可能将成为 React Components 的首选,在现阶段就让我们愉快的混合使用吧。



头图:Unsplash

作者:Rebecca

原文:https://mp.weixin.qq.com/s/I8WKWMAU_voRuLhbavpyuQ

原文:“混合双打”之如何在 Class Components 中使用 React Hooks

来源:政采云前端团队 - 微信公众号 [ID:Zoo-Team]

转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2021-07-31 08:001983

评论

发布
暂无评论
发现更多内容

软件测试/测试开发丨Selenium 高级定位 Xpath

测试人

程序员 软件测试 测试开发 selenium xpath

NFT艺术品交易平台开发搭建

V\TG【ch3nguang】

NFT数字藏品系统

AI区块链量化交易平台搭建开发

V\TG【ch3nguang】

量化交易系统开发

5 种 可帮助开发人员提高工作效率的AI 工具

3D建模设计

人工智能

免费共享大文件的10种方法,分享大文件传输的解决方案

镭速

MATLAB R2023a for Mac激活图文教程+安装密钥

胖墩儿不胖y

Mac软件 计算工具 计算软件

公链系统搭建技术方案,公链系统应用开发

V\TG【ch3nguang】

公链 区块链系统开发

区块链挖矿APP软件开发,质押流动性矿池模式系统搭建

V\TG【ch3nguang】

挖矿系统开发

生成式AI引领数据中心网络升级

百度开发者中心

#人工智能 ChatGPT 文心一言

一道经典面试题:@Configuration 和 @Component 有何区别?

江南一点雨

Java spring

NineData X SelectDB 联合发布会,8月30日即将上线!

NineData

实时数仓 数据复制 SelectDB 产品架构 NineData

WebGpu VS WebGL

3D建模设计

WebGL webgpu

19 个最佳Three.JS 示例

3D建模设计

ThreeJS

蓝易云:Ubuntu Linux中如何删除Firefox Snap?

百度搜索:蓝易云

firefox 云计算 Linux ubuntu 运维

redis分布式锁,setnx+lua脚本的java实现 | 京东物流技术团队

京东科技开发者

redis setnx Lua脚本 redis 底层原理 企业号 8 月 PK 榜

蓝易云:CentOS7系统安装和配置 DRBD?实现高可用性和数据冗余教程!

百度搜索:蓝易云

云计算 Linux centos 运维 DRBD

如何把一个接口设计好? | 京东云技术团队

京东科技开发者

接口 接口设计 企业号 8 月 PK 榜

撮合前端平台在低代码平台的落地实践 | 京东云技术团队

京东科技开发者

前端 中台架构 低代码平台 企业号 8 月 PK 榜 撮合

3种OC渲染透明背景的方法

Finovy Cloud

学习 自学 渲染 建模 渲染器

60个令人兴奋的ThreeJS网站示例

3D建模设计

three.js

区块链会员系统搭建,复利理财拆分模式开发部署

V\TG【ch3nguang】

理财系统 区块链系统开发 复利

基于深度学习的机器视觉表计识别

鲸品堂

人工智能 深度学习 数据 企业号 8 月 PK 榜

架构师日记-软件工程里的组织文化 | 京东云技术团队

京东科技开发者

架构 软件工程 自动化测试 企业号 8 月 PK 榜 组织文化

中国传统文化国际交流大会暨《传承进行时》非遗晚会启动

Geek_2d6073

成品直播源码平台消息传递的协议:MMS协议

山东布谷科技

软件开发 消息传递 成品直播源码 MMS 多媒体短信协议

GameFi模式链游搭建,养成、PK类链游系统开发

V\TG【ch3nguang】

区块链游戏 gamefi

亿级月活的社交APP,陌陌如何做到3分钟定位故障?

童子龙

微服务 性能分析 链路跟踪 可观测平台

简单好用的防火墙 Radio Silence最新激活码

mac大玩家j

Mac软件 防火墙工具 防火墙软件 阻止网络连接软件

Office 2019正式版 办公软件套装Office 2019 mac激活版

mac

苹果mac Windows软件下载 office 2019 办公软件套装

“混合双打”之如何在 Class Components 中使用 React Hooks_语言 & 开发_政采云前端团队_InfoQ精选文章