React 也能“用上” computed属性

更新日期: 2019-09-28 阅读: 6k 标签: 属性

前言,关于计算属性

初次见到计算属性一词,是在 vue 官方文档 《计算属性和侦听器》 一节中,文章中是这样描述计算属性的:

模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。

回想我们编写的 react 代码,是否也在 JSX(render 函数)中放入了太多的逻辑导致 render 函数过于庞大,难以维护?


React 中的计算属性

说到 React 之前,我们先看下 Vue,在 Vue 中,计算属性主要有以下两点特性:

  1. 计算属性以声明的方式创建依赖关系,依赖的 data 或 props 变更会触发重新计算并自动更新。
  2. 计算属性是基于它们的响应式依赖进行缓存的。

而在 React 中,计算属性也是经常可见,相信各位熟悉 React 的读者都写过类似下面的代码:

import React, { Fragment, Component } from 'react';

class Example extends Component {
  state = {
    firstName: '',
    lastName: '',
  };

  render() {
    // 在 render 函数中处理逻辑
    const { firstName, lastName } = this.state;
    const fullName = `${firstName} ${lastName}`;
    return <Fragment>{fullName}</Fragment>;
  }
}

在上面的代码里,render 函数里的 fullName 依赖了 props 中的 firstName 和 lastName 。firstName 或 lastName 变更之后,变量 fullName 都会自动更新。其实现原理是 props 以及 state 的变化会导致 render 函数调用,进而重新计算衍生值。

虽然能实现计算,但我们还是把计算逻辑放入了 render 函数导致了它的臃肿,这并不优雅。更好的做法是把计算逻辑抽出来,简化 render 函数逻辑:

class Example extends Component {
  state = {
    firstName: '',
    lastName: '',
  };

  // 把 render 中的逻辑抽成函数,减少render函数的臃肿
  renderFullName() {
    const { firstName, lastName } = this.state;
    return `${firstName} ${lastName}`;
  }

  render() {
    const fullName = this.renderFullName();
    return <Fragment>{fullName}</Fragment>;
  }
}

如果你对 Vue 很了解,你肯定知道其 computed 计算属性,底层是使用了getter,只不过是对象的 getter。那么在 React 中,我们也可以使用类的 getter 来实现计算属性:

class Example extends Component {
  state = {
    firstName: '',
    lastName: '',
  };

  // 通过getter而不是函数形式,减少变量
  get fullName() {
    const { firstName, lastName } = this.state;
    return `${firstName} ${lastName}`;
  }

  render() {
    return <Fragment>{this.fullName}</Fragment>;
  }
}


进一步,使用 memoization 优化计算属性

上文有提到在 Vue 中计算属性对比函数执行,会有缓存,减少计算。因为计算属性只有在它的相关依赖发生改变时才会重新求值。

这就意味着只要 firstName 和 lastName 还没有发生改变,多次访问 fullName 计算属性会立即返回之前的计算结果,而不必再次执行函数。

对比之下,React 的 getter 是否也有缓存这个优势??? 答案是:没有。React 中的 getter 并没有做缓存优化

不过不用失望,我们可以使用记忆化技术(memoization)来优化我们的计算属性,达到和 Vue 中计算属性一样的效果。我们需要在项目中引入 memoize-one 库,代码如下:

import memoize from 'memoize-one';
import React, { Fragment, Component } from 'react';

class Example extends Component {
  state = {
    firstName: '',
    lastName: '',
  };

  // 如果和上次参数一样,`memoize-one` 会重复使用上一次的值。
  getFullName = memoize((firstName, lastName) => `${firstName} ${lastName}`);

  get fullName() {
    return this.getFullName(this.state.firstName, this.state.lastName);
  }

  render() {
    return <Fragment>{this.fullName}</Fragment>;
  }
}


再进一步,使用 React Hooks 优化计算属性

上文在 React 中使用了 memoize-one 库实现了类似 Vue 计算属性(computed)的效果 —— 基于依赖缓存计算结果。得益于React 16.8 新推出的 Hooks 特性,我们可以对逻辑进行更优雅的封装,对 Hooks 还不够了解的小伙伴可以先阅读我们团队另一篇文章 《看完这篇,你也能把 React Hooks 玩出花》

此处,我们需要用到 useMemo。官方对 useMemo 的介绍在 这里,详情请移步查看。简单的说,就是我们传入一个 回调函数和一个 依赖列表,React 会在依赖列表中的值变化时,调用这个回调函数,并将回调函数返回的结果进行缓存:

import React, { useState, useMemo } from 'react';

function Example(props) {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  // 使用 useMemo 函数缓存计算过程
  const renderFullName = useMemo(() => `${firstName} ${lastName}`, [
    firstName,
    lastName,
  ]);

  return <div>{renderFullName}</div>;
}


总结

本文介绍了在 React 中如何实现类似 Vue 计算属性(computed)的效果 —— 基于依赖缓存计算结果,实现逻辑计算与视图渲染的解耦,降低 render 函数的复杂度。

从业务开发角度来讲,Vue 提供的 api 极大地提高了开发效率。React 虽然在某些场景下,没有官方的同类原生 API 支持,但得益于活跃的社区,工作中遇到的问题总能找到解决方案。且在摸索这些解决方案的同时,我们还能学习到诸多经典的编程思想,帮助我们更合理的运用框架,用技术解决业务问题。

原文:https://segmentfault.com/a/1190000020807336


本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/6155

相关推荐

html中的marquee属性

该标签不是HTML3.2的一部分,并且只支持MSIE3以后内核,所以如果你使用非IE内核浏览器(如:Netscape)可能无法看到下面一些很有意思的效果,该标签是个容器标签

vue里的$refs属性

vuejs的极大程度的帮助减少了对dom的操作,他主要通过添加ref属性,但是当获取this.$refs属性时,稍有不注意就会输出undefined导致我们对dom节点的操作报错。this.$refs.xxx为undefined的几种情况记录:

css的overflow属性

事实上我挺长一段时间都没弄清楚overflow:scroll与overflow:auto的差别,今天测试了一下,总算是明白了。visible: 不剪切内容。hidden: 将超出对象尺寸的内容进行裁剪,将不出现滚动条。scroll: 将超出对象尺寸的内容进行裁剪,并以滚动条的方式显示超出的内容。

css使用到的border边框属性

border 在一个声明中设置所有的边框属性。 border-bottom在一个声明中设置所有的下边框属性。border-bottom-color设置下边框的颜色。border-bottom-style设置下边框的样式。

Cookie 的 SameSite 属性

Chrome 51 开始,浏览器的 Cookie 新增加了一个 SameSite 属性,用来防止 CSRF 攻击和用户追踪。Cookie 往往用来存储用户的身份信息,恶意网站可以设法伪造带有正确 Cookie 的 HTTP 请求,这就是 CSRF 攻击。

css常用属性

text-align 属性规定元素中的文本的水平对齐方式。属性值:none | center | left | right | justify;font-size表示设置字体大小,如果设置成inherit表示继承父元素的字体大小值。

JS、Jquery中判断checkbox是否选中

attr()与prop()如何选择:attr()方法返回被选元素的属性值。prop() 方法设置或返回被选元素的属性和值。当该方法用于返回属性值时,则返回第一个匹配元素的值。当该方法用于设置属性值时,则为匹配元素集合设置一个或多个属性/值对。

css z-index属性

z-index 仅适用于定位元素。即 postition 值为 relative, absolute 和 fixed 属性;堆叠顺序是当前元素位于 z 轴上的值。值越大表示元素越靠近屏幕,反之元素越远离屏幕在同一个堆叠上下文中, z-index 值越大,越靠近屏幕。

CSS中的cursor属性

css中的cursor这个属性是用来设置光标形状的。这个属性定义了鼠标指针放在一个元素边界范围内时所用的光标的形状。默认值:auto,继承性:yes,出现版本:css2

Vue.js-计算属性和class与style绑定

所有的计算属性都以函数的形式写在Vue实例中的computed选项内,最终返回计算后的结果。在一个计算属性中可以完成各种复杂的逻辑,包括运算、函数调用等,只要最终返回一个结果即可。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!