中山公众号开发,中山小程序开发,中山企业官网开发,中山软件开发,中山APP开发
公司动态
COMPANY DYNAMIC
行业资讯
COMPANY DYNAMIC
扫一扫出方案
让你的代码讲出它的故事
点击数:
2020-06-16 09:35:19

现在用 Hooks 管理 React 函数式组件中的状态已经很容易了。我以前曾写过“用自定义 Hooks 作为服务”和“自定义 Hooks 中的函数式编程”(译文)。在本文中,我会分享自己做的一个相当简单的重构,通过重构带来了一种更整洁、可重用且更简单的实现。

本文最初发布于 Orizens 博客,经原作者 Oren Farhi 授权,由 InfoQ 中文站翻译并分享。

代码抽象

我认为代码应该是自解释的,并且能轻松到处移动和重用。有时,一种比较简单的方法是从基础的方法入手,一旦看到重复出现的模式,就可以将其抽象化。

我认为正确应用的代码抽象可以让很多事情一目了然。但抽象太多可能适得其反——很难找出实现的脉络,或者我喜欢称之为 "糟糕的诗"。

我为 ReadM™创建了Speaker()组件, ReadM™是一款免费且易用的阅读 Web 应用,它可以激励孩子们通过实时反馈来练习、学习、阅读和讲出英语,并提供了很好的体验。

让你的代码讲出它的故事

这一组件负责显示文本,且孩子读出句子或某个单词就可以用应用交互。为了改善用户体验,我决定在用户说话时添加单词高亮显示(很像卡拉 OK)。

让你的代码讲出它的故事

React Speaker 组件布局

Speaker() 组件会接收几个 props 来实现上述交互。

Speaker 的组件定义

以下是所有 props 的简要介绍:

  • text:Speaker 所显示并“讲出来”的句子(或单词)

  • onSpeakComplete:念完之后,Speaker 将调用这个回调

  • disable:禁用单击一个单词听它的发音的功能

  • verifiedtext中的一组单词,它们在当前会话期间已被念对

  • highlight:在text中之前已经念对的单词的布尔值数组

  • speed:一个数字,指示句子的读速

复制代码
function Speaker({  text,  onSpeakComplete,  disable,  verified = [],  highlight = [],  speed,}: SpeakerProps) {// code}

让你的代码讲出它的故事

Speaker 的行为和功能

接下来(该函数的主体),将定义高亮显示被念到的单词的状态,以及用于设置该单词的函数 handler。本节是很重要的一节,是本文要重点强调的内容。

复制代码
const [highlightSpoken, setHighlightSpoken] = useState<{word: stringindex: number}>()const handleOnSpeak = useCallback(() => {speak({phrase: text,speed,onEndCallback: () => {onSpeakComplete && onSpeakComplete(text)setHighlightSpoken(null)},onSpeaking: setHighlightSpoken,sanitize: false,})}, [text, onSpeakComplete, setHighlightSpoken, speed])const handleOnSelectWord = (phrase: string) => {speak({ phrase, speed, onEndCallback: noop })}

Speaker 的显示:渲染

现在代码从 props 中获取值以准备显示属性,这些属性将传递到返回渲染值内的表示组件中。

复制代码
const words = verified.length ? verified : createVerifiedWords(text, highlight)const rtlStyle = resolveLanguage(text).styleconst justify = rtlStyle.length ? "end" : "between"

返回的渲染值是:

复制代码
function Speaker(props) {// all the above code commentedreturn (<Column md="row" alignItems="center" justify={justify} className="speaker"><Row        wrap={true}        className={`speaker-phrase bg-transparent m-0 ${rtlStyle}`}      >{words.map((result, index) => (<WordResultkey={`${text}-${index}`}result={result}disable={disable}highlight={highlightSpoken && highlightSpoken.index === index}onSelectWord={handleOnSelectWord}/>))}</Row><ButtonIcondata-testid="speaker"icon="volume-up"type="light"size="4"styles="mx-md-2"disabled={disable}onClick={handleOnSpeak}/></Column>)}

整合:使用 useSpeaker() 这个自定义 Hook 来重构

虽然这个组件并不大,但它也能改得更有条理,更整洁一些。

Speaker 的行为和功能代码部分可以重用,并整合到它自己的可操作单元中。请注意,“speak()”函数在两种上下文中使用了两次——也许这里可以 DRY 它一下,换一种方法。

我们可以创建一个新的可重用挂钩——useSpeaker()。我们需要让这个 hook 接收当前念到的单词(一个状态)和speak()功能。

然后我们才能抽象出整个行为的代码,并在 Speaker 的代码中使用这个好用的小代码段:

复制代码
const { spokenWord, say } = useSpeaker({text,speed,onEnd: onSpeakComplete,})

useSpeaker()包括从Speaker组件中提取的代码。

复制代码
import React from 'react';import { speak } from '../utils/speaker.util';type TextWord = {word: string;index: number;};export default function useSpeaker({ text, speed, onEnd }) {const [spokenWord, setSpokenWord] = React.useState<TextWord>();const say = React.useCallback(() => {speak({phrase: text,speed,onEndCallback: () => {onEnd && onEnd(text);setSpokenWord(null);},onSpeaking: setSpokenWordsanitize: false,});}, [text, speed, onEnd]);return { spokenWord, say };}

现在有两个“speak()”函数调用。可以在WordResult组件内部重用新的useSpeaker() hook。
我们需要在 WordResult 中更改的是——传递speed属性,而不是传递onSelectWord()的函数 handler。使用 speed 和 result(包含“word”的对象)后,可以在 WordResult 内部重用useSpeaker的功能。

复制代码
{words.map((result, index) => (<WordResultkey={`${text}-${index}`}result={result}disable={disable}highlight={spokenWord && spokenWord.index === index}speed={speed}/>))}

让你的代码讲出它的故事

使用上面的自定义 hook——useSpeaker()后,我们成功将20行代码缩减为可重用的5行代码。最重要的是,这段代码现在有更多的语义含义,并且目标非常明确。

代码如何发声

除了让代码实现 " 讲话 " 的功能外,useSpeaker()代码重构也体现了它的字面意思——只要使用正确的术语,代码就可能在你的脑海中发出声音。

我认为,编写好函数式代码后很快就应该考虑对其迭代。在阅读代码并尝试理解它时,你的脑海中可能会出现很多问题:

  • 为什么这段代码在这里?

  • 它有什么作用?

  • 用在哪里?

  • 它试图完成什么?

对于这些问题,我通常会添加一些目标,希望能带来更好的结果:

  • 哪些代码可以不要?

  • 可以将这里的代码合并为一个简短的函数名称吗?

  • 代码的哪些部分紧密耦合在一起,进而可以组合成一个“黑匣子”?

  • 怎样让代码像诗歌 / 书籍 / 日常对话那样讲一个故事?

  • 我可以让代码讲出自己的故事吗?

请查看我们的革命性应用 ReadM™,这款程序能通过实时反馈树立儿童阅读和讲出英语的信心(更多语种正在开发中)。

我会基于 ReadM™的开发经验,撰写更多有用的文章。

文章版权归极客邦科技 InfoQ 所有

节点互动(广东)科技有限公司, 一家专注于 APP开发 + 小程序开发 + 微信开发 + 系统开发 + 网站开发 的专业互联网应用服务提供商。5年实战开发经验,高校合作基地,多年行业深耕经验,经营范围涵盖中山、珠海、江门、东莞等广东各地,节点互动助力传统行业快速转型,为众多企业提供创新性互联网应用产品。


推荐文章
微信营销推广在于坚持不懈
【摘要】腾讯公司推出微信之后,基于其庞大的忠实用户群体,微信也得到众多人的关注,也让微信成为了企业进行营销的新手段。特别是微信推出公众平台之后,营销的效果越来越明显。我们从微信营销成功的案例不难看出,微信营销推广的办法有很多,但是坚持是最重要的因素,
假如朋友圈是款独立APP
微信,作为一款“工具型”产品,最早是没有朋友圈功能的。 随着时间的推移,在用户日活超过10亿的今天,除了核心的聊天功能,公众号、小程序、支付等,逐渐构成了微信这个庞大生态的组成部分,而朋友圈也是其中的一份子。
寻找新冠致重症因素,如何通过云计算将分析时间缩短 6 倍?
阿里云高性能计算团队协助中山大学使用阿里云高性能计算服务进行生物信息科学计算,在性能与资源的双重支持下,原本每个病例的测序结果需要 12 个小时左右的分析时间,在阿里云上的缩减到了 2 个小时,大大提高了工作效率。国内疫情稍有缓和,但公共科研机构与病
做产品经理,不要忽略你的隐形用户
什么是隐形用户,隐形用户有什么用?怎么获取隐形用户?本文以亲身实践为例,回答了上述问题。做产品做了这么久,仔细想一想,大部分都是围绕已经注册过我们产品的用户来迭代产品功能,优化用户体验,希望他们能一直使用我们的产品。我们也一直在想如何能更好的服务好这
你是个伪工作者么?
你是个伪工作者么?最近看到一个因为词汇,很有意思:「Pseudo Worker」,中文直译就是「伪工作者」。工作就工作呗,咋还能伪呢?你可能会说,就是假装工作呗。并不是这样。 什么是「伪工作者」,这个概念最早来自谷歌、Facebook。吴军
完成教育行业的互联网改造还需要多久?
教育培训行业正在被降维打击,环境和行业都在挤兑着教育培训机构的发展。那么,完成教育行业的互联网改造还需要多久?01 环境威胁6月17日北京以及上调防控等级到二级,18日,也就是今天北京新增21个确诊,天津1个,河北2个。原本4月末已经基本出清的疫情又
http 四种鉴权方式简介,未来可能还会出现第 5 种鉴权方
因为 http 协议,它是一种无状态的协议,在服务器端并不知道客户的那一头,是谁在请求服务器。而服务器上的资源,有时候并不是向所有人开放的,而是仅对部分人开放的,在这种情况下,实现用户的登陆鉴权,就成了一种必要的需求。目前,我们在开发中主要使用过 4
你知道,微信小程序开发有那些优点吗
我们聊天小程序是什么? 它是一个超级APP,基于WeChat平台无需下载就可以满足很多APP满足大家的需求,正因为如此,很多APP也在WeChat中开发了自己的小程序,可以让用户更方便的使用,想必大家对WeChat小程序开发应该是比较陌生的,没关系,
S2B、S2B2C傻傻分不清楚?企业S2B系统实际为S2B2
【摘要】互联网行业趋势发展中被誉为未来5年领先的商业模式:S2B,其实说白了就是供应平台的领先,独立S2B系统是一个大数据化的平台,在与协同网络相互合作的基础上来实现S2B2C的商业系统模式,所以S2B商城系统更完整的理解应该为S2B2C的创新模式互
Slack 的开发环境是如何演进的?
在本文中,开发环境是指可以在部署之前测试代码更改的沙箱,不是 Eclipse 或 Microsoft Visual Studio 这样的集成开发环境(IDE)。本文最初发布于 Slack 官方博客,由 InfoQ 中文站翻译并分享。对我来说,开发环境
在线客服系统