[React 进阶系列] 组成与继承

Posted GoldenaArcher

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[React 进阶系列] 组成与继承相关的知识,希望对你有一定的参考价值。

[React 进阶系列] 组成与继承

组成(Composition) 与 继承(Inheritance) 是两种比较常用的代码复用方案。

继承

传统的 OOP 语言之中继承的用法较多,如比较传统的动物案例:

动物 脊椎动物 无脊椎动物 哺乳类 爬行类 犬科

继承就代表子类具有父类的所有特征,如哺乳类、爬行类都属于有脊椎动物,那么他们就一定会有脊椎。哺乳类属于恒温动物,犬科作为哺乳类的一员,自然也属于恒温动物。

继承是一个用的很好能够简化结构与实现的方案,但是对于结构与架构都非常有挑战的实现。稍一不小心就会尾大不掉,子类中可能继承过多不需要的特性,如:

形状 矩形 三角形 正方形

这个设计就不是一个非常合理的设计,比较合理的设计应该让正方形直接继承形状。

通常情况下让正方形继承矩形不会有什么大问题,但是,如果有一个需求是让矩形的面积增加 10%这样一个需求,那么矩形就需要实现以下代码 [1]

public void IncreaseRectangleSizeByTenPercent(IEnumerable<Rectangle> rectangles)

  foreach(var rectangle in rectangles)
  
    if (typeof(rectangle) == Rectangle)
    
      rectangle.Length = rectangle.Length * 1.1;
    
    if (typeof(rectangle) == Square)
    
      rectangle.Length = rectangle.Length * 1.04880884817;
    
  

这时候的正方形已经不具备矩形所有的特性,强行让正方形继承矩形的意义也不大。

当然,不管继承怎么好或不好,React 官方团队的态度是:

We haven’t found any use cases where we would recommend creating component inheritance hierarchies.

即,他们到目前为止也没在 React 里面用上继承。

组成

React 文档中将组成分为了两个部分,容器化与特殊化。

容器化

组成是 React 所推荐的使用方法,在这种情况下,父组件不需要子组件的实现,只需负责将其渲染出来即可。最简单的例子是创建一个 Modal 组件,对于 Modal 组件来说,它并不需要在乎子组件会包含什么——可以是一个表单,可以是一段信息,又或者是其他根据需求需要渲染的内容。对于 Modal 而言,它需要实现的功能有两个:

  1. 将传递进来的子组件渲染出去
  2. 实现 Modal 的必要功能,如点击背景时关闭 Modal

这个时候使用继承就不是一个非常合适的方案,组成就是一个更加合适的方案。

Modal 的实现如下:

import  useState  from "react";

const Modal = ( show, children ) => 
  const [isShown, setIsShown] = useState(show);

  const closeModal = () => 
    setIsShown(false);
  ;

  if (!isShown) 
    return null;
  

  return (
    <div className="modal" onClick=closeModal>
      children
    </div>
  );
;

export default Modal;

这个 Modal 实现了两个基础功能,点击“背景”时关闭 Modal,以及渲染传递进 Modal 的子组件。这个时候,作为父组件的 Modal 可以不需要在意子组件究竟是什么,只需要单纯将其渲染出去即可,如:

import  StrictMode  from "react";
import  createRoot  from "react-dom/client";
import "./styles.css";

import App from "./App";
import Modal from "./Modal";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <StrictMode>
    <a href="">It's another message</a>
    <Modal show=true>
      /* 这个App里面就yiju hello,不过不管里面传递什么,Modal都能渲染出来 */
      <App />
    </Modal>
  </StrictMode>
);

其渲染结果如下:

Modal 只是一个案例显示了组成的用途,其主要功能也是创建单独的最小子组件,尽最大程度的提升可复用性,减少重复代码。

特殊化

这种场景多运用于有些容器化的组件可能会被重复使用多次,依旧以上面的 Modal 为案例,假设业务需求是当用户需要注册时,不选择渲染一个单独的注册页面,而是跳出一个注册 Modal。而注册 Modal 应用的场景在于:用户主动点击注册按钮,没有帐户的用户再进行下载资源、阅读文章等操作时选择注册。这时候注册 Modal 就会在各个页面跳来跳去。

又因为注册只会出现在 Modal 中(这只是一个假设性的需求),所以根据同样的代码在项目中出现两次就需要封装的原则,这时候封装一个特殊化的注册 Modal 就是一个更好的选择。

RegisterModal 的实现大致如下:

const RegisterModal = () => 
  // 省略表单之类的逻辑

  return (
    <Modal show=true>
      <form>/*  省略注册表单的实现  */</form>
    </Modal>
  );
;

这样,当有用户通过点击事件打开 RegisterModal 时,开发者需要做的就是直接渲染 <RegisterModal /> 即可。

参考

[1]: Why would Square inheriting from Rectangle be problematic if we override the SetWidth and SetHeight methods?

[2]: Composition vs Inheritance

以上是关于[React 进阶系列] 组成与继承的主要内容,如果未能解决你的问题,请参考以下文章

[React 进阶系列] Functional Component 与 Class Component 中使用 Context

[React 进阶系列] Functional Component 与 Class Component 中使用 Context

python 单继承多继承菱形继承

Python初识类与对象

Python初识类与对象

React.js 新手快速入门 - 进阶篇