反应设计模式的演变:从HOC到钩子和自定义钩
#javascript #react #designpatterns #hooks

“进化就是找到更好的做事方式。” - 丹·阿布拉莫夫(Dan Abramov)

在反应开发的早期,开发人员面临着无数的挑战。反应应用程序变得越来越复杂,并且与组件重用,状态管理和代码可维护性有关的问题成为常见的对手。 React的核心团队拥有著名的成员,例如Dan Abramov,SebastianMarkbã¥GE,Sophie Alpert和Andrew Clark,他们认识到这些挑战,并着手寻求发掘更好的设计模式。本文将带您穿越反应设计模式的演变,从高阶组件(HOC)到React Hooks的开创性时代和自定义挂钩的兴起。

第1章:高阶组件(HOC)模式

需要HOC的问题

在早期的反应时代,组件再利用是一个巨大的障碍。管理共享状态是迷宫般的任务,代码库正在发展为复杂性的纠结网。包括有影响力的丹·阿布拉莫夫(Dan Abramov)在内的React团队承认了这些问题,并着手寻求解决方案。

丹·阿布拉莫夫(Dan Abramov)在他的开创性文章中强调了组件中混合数据提取和渲染逻辑的问题:“ 提取组件的数据依赖性使您可以用不同的数据源重复使用呈现组件。”。”是反应模式演变中的关键时刻。

输入高级组件(HOCS)

为了解决当前的问题,高阶组件(HOC)的概念诞生了。 HOC允许开发人员包装组件,从而增强其功能和可重复性。使用HOC,可以在整个应用程序中抽象和应用共享的逻辑和行为。

这是一个简单的事实的现实示例,它将身份验证逻辑添加到组件:

import React, { Component } from 'react';

const withAuthentication = (WrappedComponent) => {
  class WithAuthentication extends Component {
    constructor(props) {
      super(props);
      this.state = {
        isAuthenticated: false,
      };
    }

    componentDidMount() {
      // Check authentication logic here
      const isAuthenticated = // Your authentication check logic
      this.setState({ isAuthenticated });
    }

    render() {
      if (!this.state.isAuthenticated) {
        return <div>Authentication failed. Please log in.</div>;
      }
      return <WrappedComponent {...this.props} />;
    }
  }

  return WithAuthentication;
};

export default withAuthentication;

HOC的优点

Hocs赋予了反应开发,具有多个优势。它们通过启用具有特定行为的组件包装来促进代码重复使用。例如,可以使用 withAuthentication HOC来保护需要身份验证的路线。开发人员陶醉于HOC提供的灵活性和合成性。

丹·阿布拉莫夫(Dan Abramov):“高阶组件(HOCS)允许您重复使用组件逻辑。这是通过将组件包裹在返回具有所需行为的新组件的函数中来实现的。” < < /p>

HOC的缺点和弱点

但是,HOC并非没有他们的挑战。用HOCS包装组件会导致大量的包装组件,从而产生复杂的层次结构。调试和理解组件结构变得越来越艰巨,“包装纸地狱”一词在反应话语中找到了地位。

丹·阿布拉莫夫(Dan Abramov):“ Hocs没有解决所有问题。它们可以通过引入不必要的抽象层来使您的代码更加复杂,而且它们可能很难理解。”

第2章:演示和容器模式

过渡到演示和容器

随着React社区应对HOC的局限性,出现了一种新模式:演示和容器模式。这种模式试图解决HOCS在保留其优势的同时带来的问题。它将组件划分为两个不同的类别:负责渲染的演示组件,以及负责数据逻辑的容器组件。

这是演示组件的示例:

import React from 'react';

const PresentationComponent = ({ data }) => (
  <div>
    <h1>Data Presentation</h1>
    <p>{data}</p>
  </div>
);

export default PresentationComponent;

和管理状态和逻辑的容器组件:

import React, { Component } from 'react';

class ContainerComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: '',
    };
  }

  componentDidMount() {
    // Fetch data and update state here
    const data = // Your data fetching logic
    this.setState({ data });
  }

  render() {
    return <PresentationComponent data={this.state.data} />;
  }
}

export default ContainerComponent;

演示和容器的优点

演示文稿和容器模式将清晰度引入了React代码库中。演示成分演变成专门用于渲染的纯净无状态组件。容器组件扮演了管理数据逻辑和维护组件状态的作用。这种隔离简化了测试和支持代码可维护性。

SebastianMarkbã¥ge:“演示文稿和容器模式的目标是改善组件的可重复性,可检验性和整体组织。”

演示和容器的缺点和弱点

尽管如此,这种模式不是灵丹妙药。开发人员经常发现自己为单个演示文稿组件创建多个容器组件,从而导致大量文件和复杂性升级。这种模式虽然有所改进,但仍有其复杂性和挑战可应对。

Sophie Alpert:“有时您可能需要编写一个容器组件,即使您不觉得它重复使用任何逻辑,没关系。这与您重复使用多少逻辑无关,而是您可以重复使用多少逻辑一眼了解。”

第3章:React钩的出现

钩子简介

React景观经历了巨大的转变,随着React钩子的出现。将钩子设想为HOCS和表现和容器模式所带来的挑战的一种补救措施。他们授权功能组件来管理状态和副作用,而无需集体组件。

这是使用 useState useEffect 钩子的功能组件的示例:

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

const HooksComponent = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

export default HooksComponent;

React Hooks的优点

反应引诱了一个简单性和可重复性的新时代。它们使功能组件能够轻松地管理局部状态,副作用和上下文。简明的语法和消除了开发人员共鸣的类组件,从而产生更清洁和更可读的代码。

丹·阿布拉莫夫(Dan Abramov):“ React Hooks的目的是以一种感觉更自然并且更少的权衡方面解决渲染道具和高阶组件的问题。”

React钩子的缺点和弱点

尽管受到广泛赞誉,但React Hooks并不完全没有挑战。将现有的类组件调整为具有钩子的功能组件所需的精力和考虑。另外,某些生命周期方法,例如 getSnapshotBeforeUpdate ,仍然是类组件的独有方法。

索菲·阿尔珀特(Sophie Alpert):“钩子还不能涵盖所有用例,而且它们可能永远不会涵盖所有用例。这就是为什么我们不贬低课程。目标是提供替代方案,而不是迫使人们去改变他们的习惯。”

第4章:自定义挂钩的上升

自定义挂钩作为模式

React社区对创新的无限胃口为自定义挂钩模式铺平了道路。自定义挂钩代表了反应设计模式的范式转移,使开发人员能够以无与伦比的简单性将组件封装和共享逻辑。

以下是管理切换状态的自定义挂钩的示例:

import { useState } from 'react';

const useToggle = (initialValue = false) => {
  const [value, setValue] = useState(initialValue);

  const toggle = () => {
    setValue(!value);
  };

  return [value, toggle];
};

export default useToggle;

定制钩的优点

自定义挂钩提供了用代码组织和重用的最终工具提供的React开发人员。他们解锁了逻辑抽象和分布的潜力,将复杂的成分转化为自定义钩的简洁组成。新发现的模块化升高的反应应用到可维护性的新高度。

安德鲁·克拉克(Andrew Clark):“自定义挂钩让您将组件逻辑提取到可重复使用的功能中。这对于跨组件(例如表单处理或动画)共享行为特别有用。”

定制钩子的缺点和弱点

虽然自定义钩子大大富集了反应生态系统,但它们不是银弹。跨代码库的自定义钩的扩散保证了周到的命名和文档。开发人员需要确保自定义挂钩的目的和用法得到充分理解以防止混乱。

安德鲁·克拉克(Andrew Clark):“自定义钩子可以是双刃剑。适当地使用时,它们可以使您的代码更有条理和重复使用。但是,滥用或过度使用会导致混乱和不必要的复杂性。” <<<<<<<<<<<<<<<<<<<<<<<<< /strong>

第5章:持续的进化

随着反应生态系统的成熟,设计模式和最佳实践的景观不断发展。让我们仔细研究这些模式如何适应并探索现实世界中证明其持续发展的示例。

1.高阶组件(HOC)模式

进化:渲染道具

曾经是反应发展的基石的事件模式,已经看到了呈现更灵活的模式,例如渲染道具。尽管HOC仍然相关,但渲染道具提供了一种更直观的方式来共享组件之间的代码。

示例

考虑需要获取和显示数据的常见场景。您可以使用渲染道具组件:
而不是将组件包装到HOC中

class DataFetcher extends React.Component {
  state = {
    data: null,
  };

  componentDidMount() {
    // Fetch data and update the state
  }

  render() {
    return this.props.render(this.state.data);
  }
}

<DataFetcher render={(data) => <DisplayData data={data} />} />

渲染道具提供了更大的组件合成性并减少了高阶组件的嵌套。

2.演示和容器图案

进化:组成部分

虽然表现和容器模式强调了关注点的分离,但现代反应开发促进了组成的组成。组成较小,可重复使用的组件已成为一种流行的做法。

示例

您可以从较小的组件中构成UI,而不是创建单独的容器组件:

function WeatherDisplay({ city }) {
  const { data, error, loading } = useWeatherData(city);

  if (loading) {
    return <LoadingSpinner />;
  }

  if (error) {
    return <ErrorMessage error={error} />;
  }

  return <WeatherInfo data={data} />;
}

这种方法增强了可重用性并简化了组件层次结构。

3.反应钩

进化:并发模式

React钩子代表了一个关键的进步,但进化并没有停止。并发模式(一种实验性React功能)设置为通过允许React同时处理多个任务,以进一步改善用户体验。

示例

假设您正在构建资源密集型应用程序。并发模式可以帮助确定关键组件的渲染更新,确保用户体验更顺畅。

function ResourceIntensiveComponent() {
  // Resource-intensive rendering logic
}

export default React.memo(ResourceIntensiveComponent, (prevProps, nextProps) => {
  // Implement custom comparison logic
  return prevProps.data === nextProps.data;
});

通过实现自定义比较逻辑,并发模式可以将资源集中在最需要的地方。

4.自定义钩

进化:外部图书馆和社区

自定义钩子已授权开发人员创建自己的图书馆并与社区分享。随着React的生态系统的扩展,外部库提供了更强大和专业的解决方案。

示例

想象您正在从事一个需要复杂状态管理的项目。您可以利用Redux或MobX等外部库,而不是重新发明轮子,这些库提供了强大的状态管理解决方案。

import { useSelector, useDispatch } from 'react-redux';

function Counter() {
  const count = useSelector((state) => state.counter);
  const dispatch = useDispatch();

  return (
    <div>
      <span>Count: {count}</span>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
    </div>
  );
}

通过拥抱外部图书馆和社区驱动的解决方案,您可以利用React社区的集体专业知识。

结论

通过React设计模式的旅程证明了Web开发的动态性质。从零件的早期斗争到钩子的曙光,React的进化是非凡的。当您浏览React项目时,请记住这些模式的丰富历史,并挥舞它们以制作可维护,高效和可扩展的应用程序。

React团队的各种见解

为了深入了解React团队的观点,请深入研究他们的文章和观点:

Dan Abramov on Presentational and Container Components
Sebastian Markbåge on React Component Patterns
Sophie Alpert on Component Patterns and Composition
Andrew Clark on the Adoption and Impact of Hooks
这些资源为塑造了反应的设计模式的动机和哲学提供了宝贵的见解。