换个姿势看 hooks,灵感来源组合和 HOC 模式下逻辑视图分离新创意

d8c56793148a23579d56941cfb816f8d.gif

作者 | 👽

来源 | 前端Sharing

3e05d41dec5c828e2d84349d755a37cc.png

前言

懂得 JSX 本质的同学都知道它只不过是一种语法糖,会被 babel 处理成 createElement 的形式,最后再变成常规的 js 对象。所以,我们就可以在 js 逻辑层面对  element 对象做处理,自定义 hooks 作为 element  逻辑处理层,也就变得理所当然了。

本文我们就来研究一下,自定义 hooks 的一些其他的用途,以及怎么样处理视图层,还有一些新玩法。

8bb86858f01f2ad113c00a6c1a91f3b2.png

用 hooks 处理 element 对象


场景一

hooks 处理 element 的案例已经屡见不鲜了。比如我们相对一些 UI 层的内容做缓存处理,像如下场景。

function Test(){console.log('test rerender')return <div>hello, react !</div>
}function Index({ value }){const [ number , setNumber ] = React.useState(0)const element = React.useMemo(()=> <Test /> ,[ value ])return <div>{element}<button onClick={() => setNumber(number +1 )} > 点击 {number} </button></div>
}

如上用useMemo缓存处理 Test 组件对应的 element 对象,之后当 Index 中的 value 改变的时候,才会再次执行 useMemo 得到新的 element 对象。

当点击按钮的时候,会触发 setNumber 改变 state,会触发 Index 的更新,但是 useMemo 会直接读取缓存的值,这样性能上的体验就是 Test 不会再更新。

这是一种基于 hooks 的实现的优化策略,本质上是对 element 的缓存。这种方案处理后 Index 不再需要类似于 HOC 的 memo 组件包裹。可以根据条件和方向,做渲染上定制方向上的优化,这是一种父 -> 子的优化方案。

还有一些更为复杂的场景,就是多个 hooks 组合起来,来达到目的。

function Index(){const [ number , setNumber ] = React.useState(0)const { value } = React.useContext(valueContext)const element = React.useMemo(()=> <Test /> ,[ value ])return <div>{element}<button onClick={() => setNumber(number +1 )} > 点击 {number} </button></div>
}

通过useContext读取valueContext中的 value 属性, Test 组件订阅 value 的变化,当 context 里面的 value 改变的时候,重新生成 element 对象,也就是重新渲染 Test 组件。

场景二

react router v6 出来之后,有一个全新的 hooks —— useRoutes。它可以接受路由的配置的 js 路由树,返回一个视图层的 element tree。我们看一下具体使用。

const routeConfig = [{path:'/home',element:<Home />},{path:'/list/:id',element:<List />},{path:'/children',element:<Layout />,children:[{ path:'/children/child1' , element: <Child1/> },{ path:'/children/child2' , element: <Child2/>  }]}
]const Index = () => {const element = useRoutes(routeConfig)return <div className="page" ><div className="content" ><Menus />{element}</div></div>
}
const App = ()=> <BrowserRouter><Index /></BrowserRouter>

useRoutes 为自定义 hooks ,返回规范化的路由结构。hooks 不再像我们平时那样只负责逻辑的处理,此场景下,hooks 完全充当了一个视图容器。

这个模式下,对自定义 hooks 理解打破了传统观念,可能这种由逻辑层到视图层的转化,会让一部分同学不适应,不过这些不重要,我们要有一个思维上的转变,这才显得重要。

383d78682135ca2a5f7326741ac02a0d.png

设计模式

下面设想一个场景,自定义 hooks 可不可以实现一种设计场景,可以类似于组合模式和 hoc 模式的结合,可以实现逻辑和视图的分离呢?

1、传统的组合模式缺点

首先看一下组合模式,传统的组合模式如下所示:

function Index(){return <GrandFather><Father><Son>{null}</Son></Father></GrandFather>
}

上面通过 GrandFather , Father, Son 三个组件进行组合模式。这种模式下,组合的内外层组件需要建立关联和通信的话,需要通过 cloneElement 混入一些通信的方法。

以上面为例子,如果想要实现 Father <——> Son 双向通信,我们需要这么处理:

/* 父组件 */
function Father({ children }){const [ fatherSay , setFatherSay ] = React.useState('')const toFather= ()=> console.log('son to father')const element = React.cloneElement(children,{ fatherSay ,toFather })return <div><p> Father </p><button onClick={() => setFatherSay('father to son')} >to Son</button>{element}</div>
}/* 子组件 */
function Son({ children, fatherSay, toFather }){console.log(fatherSay)return <div><p> son </p><button onClick={toFather} >to Father</button>{children || null}</div>
}

如上

  • Father 组件通过 cloneElement 向 props 中混入 toFather 方法。Son 组件可以直接通过 props 拿到此方法向父组件通信,实现 Son -> Father

  • Father 可以通过 useState 改变 fatherSay 并且传递给 Son,实现 Father -> Son

有一个显而易见的弊端就是:

toFathercloneElement 等逻辑需要开发者去单独处理,也就是逻辑层和 ui 层是强关联的。这就需要开发者,在组合模式的上下层组件中分别处理逻辑。

如果再加上 GrandFather 组件,那么就需要像下图一样处理:

ea646f5727506e0f7cf881fc953e06d4.png

2、hoc 嵌套提供 idea

hoc 本身就是一个函数,接收原始组件,返回新的组件,多个 hoc 可以嵌套。

function Index(){/* .... */
}
export default HOC1(styles)(HOC2( HOC3(Index) ))

HOC1 -> HOC2 -> HOC3 -> Index

那么可不可以用 hoc 这个思想,来实现组合模式呢,并且解决逻辑冗余呢。

3、用自定义 hooks 实现

结合最开始讲到的,可以通过自定义 hooks 来处理 ui 逻辑,那么就能通过类似 hoc 的多层嵌套 hooks,解决组合模式的上述缺陷。

那么自定义的 hooks 的设计如下:

useComposeHooks( component, Layout , mergeProps )
  • component 为需要通过组合模式处理的组件。

  • 需要组合的容器组件。

  • mergeProps 需要合并的新的 props 。

  • useComposeHooks 可以多个嵌套使用。比如如下:

function Index(){const element = useComposeHooks( useComposeHooks( useComposeHooks(...) , Layout2,mergeProps ) ,Layout1,mergeProps)return element
}

等价于:

<Layout1><Layout2>{ ... }</Layout2>
</Layout1>

接下来我们去实现这个功能。

351a4348940ad0027a534575e151ed1c.png

代码实现及效果验证


1、编写 useComposeHooks

接下来我们编写一下 useComposeHooks:

function useComposeHooks(component, layout, mergeProps) {const sonToFather = useRef({})const fatherToSon = useRef({})/* 子对父组件通信  */const sonSay = React.useCallback((type, payload) => {const cb = sonToFather.current[type]typeof cb === 'function' && cb(payload)}, [component])/* 父监听子组件 */const listenSonSay = React.useCallback((type, fn) => {sonToFather.current[type] = fn}, [layout])/* 父对子组件通信*/const fatherSay = React.useCallback((type,payload)=>{const cb = fatherToSon.current[type]typeof cb === 'function' && cb(payload)},[layout])/* 子监听父组件 */const listenFather = React.useCallback((type,fn)=>{fatherToSon.current[type] = fn},[ component ])const renderChildren = React.useMemo(() => {return component ? React.cloneElement(component, { listenFather, sonSay }) : null}, [component])return layout ? React.createElement(layout, { fatherSay,listenSonSay, ...mergeProps, children: renderChildren }) : renderChildren
}
  • 通过 useRef 保存通信方法。

  • 编写 sonSay (子对父组件通信),listenSonSay (父监听子组件),fatherSay(父对子组件通信),listenFather(子监听父组件)方法。

  • 通过 cloneElement 克隆内层组件。

  • 通过 createElement 创建外层组件。

2、测试  demo

function GrandFather({ name, children }) {return <div><p> {name} </p>{children}</div>
}function Father({ children, listenSonSay, name ,fatherSay}) {listenSonSay('sonSay', (message) => console.log(message))return <div><p> {name} </p><button onClick={() => fatherSay('fatherSay','hello,son!')} >to Son</button>{children}</div>
}function Son({ children, sonSay,listenFather ,name }) {listenFather('fatherSay',(message) => console.log(message) )return <div><p> {name} </p><button onClick={() => sonSay('sonSay', 'hello,father!')} >to Father</button>{children || null}</div>
}
export default function Index() {return (useComposeHooks(useComposeHooks(useComposeHooks(null, Son, { name: 'Son' }), Father, { name: 'Father' }), GrandFather, { name: 'GrandFather' }))
}
  • 如上,我们不再需要向业务层做其他的处理。只需要调用 props 里面的相关方法就可以了。

接下来看一下效果(非动图):

f5c13d69f2a63b01ad95d991ae8fe2a7.png


如上,完美实现了。通过这个案例,主要向大家展示自定义 hooks 实现了组合模式。不要太关注代码的细节。

aa11b8f8b1ada47b2e63319f6edd4d2f.png

总结

今天通过一个创意想法讲述了自定义 hooks 的一些其他玩法,当然本文中的 demo 只是一个案例,并不能使用在真实的业务场景下,通过本文希望大家对 hooks 有一个全新的理解。

6dea39db1c85c5fe0bf7d8e11b933358.gif

往期推荐

如果让你来设计网络

Docker:从入门到实战过程全记录

没有操作系统程序可以运行起来吗?

如何在 Kubernetes Pod 内进行网络抓包

ac56163619b264dd63968c8c88ea3b3c.gif

点分享

f90d74b35fb35f4a411491b1af07ac1e.gif

点收藏

aff9dfa725888aaf524b0f6ee1e4a716.gif

点点赞

f16af33db7be758d7fb5b66f0bab45b0.gif

点在看

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/511900.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

双11特刊 | 云数据库RDS如何顺滑应对流量洪峰

简介&#xff1a;从绿色低碳到硬核科技&#xff0c;看RDS如何用绿色科技助力2021“双11”&#xff1f; 双十一回顾 从平台到商家&#xff0c;再从物流到客户手中&#xff0c;云数据库RDS支撑着双11集团电商的在线业务。RDS首次对集团核心业务进行国产化技术演进试点&#xff…

双11专刊|云原生数据仓库AnalyticDB支撑双11,大幅提升分析实时性和用户体验

简介&#xff1a;2021年双十一刚刚落幕&#xff0c;已连续多年稳定支持双十一大促的云原生数据仓库AnalyticDB&#xff0c;今年双十一期间仍然一如既往的稳定。除了稳定顺滑的基本盘之外&#xff0c;AnalyticDB还有什么亮点呢&#xff1f;下面我们来一一揭秘。 一 前言 2021年…

html传输的数值表示的含义,数字传递游戏的意义与感悟_传数字游戏心得体会

在大学生入职培训期间&#xff0c;曾组织他们做了一场小游戏&#xff0c;游戏规则如下&#xff1a;1、80名学生平均分成8组&#xff0c;排成8列&#xff0c;统一面向讲台做好&#xff1b;2、主持人向每组的最后一名队员提供一个数字(数字一般为3位或4位数&#xff0c;不确定&am…

德勤2022技术趋势:IT自我颠覆、技术跨界融合创新

作者 | 宋慧 出品 | CSDN云计算 IT 技术&#xff0c;一直处于快速发展与变化中。 基于对前沿技术的观察分析与自身实践&#xff0c;国际机构德勤管理咨询每年发布对于未来 18-24 个月的的重要技术趋势。2021 年 CSDN 曾报道 德勤2021技术趋势&#xff1a;繁琐、点状的匠人AI时…

双11特刊|购物车实时显示到手价,看云原生内存数据库Tair如何提升用户体验?

阿里云自研内存数据库Tair诞生于2009年&#xff0c;是一种支持高并发低延迟访问的云原生内存数据库&#xff0c;完全兼容Redis&#xff0c;已历经多年双11大促考验&#xff0c;提供核心在线访问加速能力&#xff0c;显著提升系统吞吐量。 作为双11大促承载流量洪峰的利器&…

Dubbo-Admin 正式支持 3.0 服务治理

简介&#xff1a;Dubbo 相信大家并不陌生&#xff0c;是一款微服务开发框架&#xff0c;它提供了 RPC 通信与微服务治理两大关键能力。大家在日常开发中更多使用的是 Dubbo 提供的 RPC 通信这一部分能力&#xff0c;而对其提供的服务治理的能力使用相对少一些&#xff0c;本文的…

vue将文本渲染html,vue2.0 之文本渲染-v-html、v-text

vue2.0 之文本渲染-v-html、v-text1、index.html代码vuedemo2、main.js代码import Vue from ‘vue‘import App from ‘./App‘Vue.config.productionTip false/* eslint-disable no-new */new Vue({el: ‘#app‘,render: h > h(App)})render: h > h(App)是ES6的语法&am…

如何成为真正的数字化企业,锐捷网络发布数字原力觉醒计划

编辑 | 宋慧 出品 | CSDN 云计算 什么样的企业可称为数字化企业&#xff1f; 因为疫情等各类不确定因素&#xff0c;数字化的浪潮正深刻改变着企业。所有企业都需考虑转型、创新、增长&#xff0c;这三个问题。深耕中国企业级市场多年的IT技术厂商锐捷网络&#xff0c;以“点线…

2021中国数字服务大会 | 阿里云混合云新一代运维演进与实践

简介&#xff1a;12月3日&#xff0c;2021中国数字服务大会顺利召开&#xff0c;大会以“数字服务、跨界融合、协同创新”为主题&#xff0c;邀请产学研界嘉宾&#xff0c;举办行业与学术论坛&#xff0c;共话数字服务的挑战和机遇。阿里云作为云厂商代表应邀参会&#xff0c;并…

冲压模板自动标注LISP_干货满满!超实用冲压模具资料,加薪必看!

一般的冲压模具都是由&#xff1a;上下托板、上下垫脚、上下模座&#xff1a;一般用A3、Q235等“软料”做成&#xff0c;起支撑整个模具、方便架模、落料等作用。上、下模板&#xff1a;上、下模板起固定刀口、入块、入子、顶料销等作用&#xff0c;外定位、内定位、浮升引导销…

安谋科技四周年献礼,提前完成五年规划目标

自2018年4月正式独立运营以来&#xff0c;安谋科技一直以服务中国的科技产业、建设中国本土的研发能力、赋能中国本土半导体生态为核心使命。值此公司成立四周年之际&#xff0c;安谋科技宣布已提前超额完成了合资公司落地深圳时设立的五年规划目标。 回顾四年来走过的历程&am…

开源微服务编排框架:Netflix Conductor

简介&#xff1a;本文主要介绍netflix conductor的基本概念和主要运行机制。 作者 | 夜阳 来源 | 阿里技术公众号 本文主要介绍netflix conductor的基本概念和主要运行机制。 一 简介 netflix conductor是基于JAVA语言编写的开源流程引擎&#xff0c;用于架构基于微服务的流…

直播回顾:如何对付臭名昭著的 IO 夯?诊断利器来了 | 龙蜥技术

简介&#xff1a;听到IO夯总是让人头疼&#xff0c;那有没有可以分析IO夯问题的利器&#xff1f; 编者按&#xff1a;sysAK&#xff08;system analyse kit&#xff09;&#xff0c;是龙蜥社区&#xff08;OpenAnolis&#xff09;系统运维 SIG 下面的一个开源项目&#xff0c;…

cad致命错误如何处理_Golang 如何优雅地处理错误

- 后端早读课翻译计划 第二篇- 本文提供了一个优雅的处理 Golang 中错误的方法&#xff0c;解决了 Golang error 只有字符串信息的局限性&#xff0c;提供了上下文信息、错误类型判断的功能。尽管 go 具有一个简单的错误模型&#xff0c;但是乍一看&#xff0c;事情并没有那么容…

快速云原生化,从数据中心到云原生的迁移实践

简介&#xff1a;本文将介绍在帮助用户快速完成迁云中的解决方案、最佳实践以及迁云工具。 云原生的时代已经到来&#xff0c;云原生技术正在重塑整个软件生命周期&#xff0c;阿里巴巴是国内最早布局云原生技术的公司之一。 容器服务团队在过去的几年时间内帮助很多用户成功…

实力总结四类 Bean 注入 Spring 的方式

作者 | 阿Q来源 | 阿Q说代码一提到Spring&#xff0c;大家最先想到的是啥&#xff1f;是AOP和IOC的两大特性&#xff1f;是Spring中Bean的初始化流程&#xff1f;还是基于Spring的Spring Cloud全家桶呢&#xff1f;今天我们就从Spring的IOC特性入手&#xff0c;聊一聊Spring中把…

广州大学计算机网络期末考试2013,广州大学计算机网络技术试卷(A卷)

广州大学2006-2007 学年第 1 学期考试卷课程计算机网络技术考试形式(开/闭卷&#xff0c;考试/查)一、填空题(15分)1、现有一计算机要和另一设备进行通信&#xff0c;要实现此目标首先要考虑通过何种接口把计算机和相应设备连接起来&#xff1b;解决此问题属于层的任务。(2分)2…

阿里云全站加速DCDN升级

简介&#xff1a;相比传统CDN加速&#xff0c;全站加速DCDN具有更广阔的应用场景。在当下企业全面数字化的进程中&#xff0c;为了更全面地满足广大企业客户的个性化加速需求&#xff0c;全站加速DCDN从简单开通到个性化定制、从内容分发到安全防护&#xff0c;对客户侧的使用体…

Redis 内存满了怎么办?这样置才正确!

作者 | 码哥呀来源 | 码哥字节上回在《Redis 数据过期了会被立马删除么&#xff1f;》说到如果过期的数据太多&#xff0c;定时删除无法删除完全&#xff08;每次删除完过期的 key 还是超过 25%&#xff09;&#xff0c;同时这些 key 再也不会被客户端请求&#xff0c;就无法走…

2000坐标系高程与85高程转换_【科普】测量人必须知道的几大坐标系

导读 不了解坐标系的测绘测量人员,不是好员工!无论科研的还是外业的,亦是如此。小编今天就为大家盘点出这几大传统坐标系,各位大大赶快收藏起来吧! 1 北京54坐标系 新中国成立以后,我国大地测量进入了全面发展时期,在全国范围内开展了正规的,全面的大地测量和测图工作,…