如何使用 React Hooks 重构类组件?(上)

简介: 如何使用 React Hooks 重构类组件?

最初,在 React 中可以使用 createClass 来创建组件,后来被类组件所取代。在 React 16.8版本中,新增的 Hooks 功能彻底改变了我们编写React程序的方式,因为使用 Hooks 可以编写更简洁、更清晰的代码,并为创建可重用的有状态逻辑提供了更好的模式。

许多公司和开发人员都放弃了类组件转而使用 Hooks。而许多旧的的React 项目仍然在使用类组件。更重要的是,在类组件中有 Error Boundaries,而函数组件中是无法使用 Error Boundaries 的。

本文就来通过一些常见示例看看如何使用 React Hooks 来重构类组件。


1. 管理和更新组件状态


状态管理是几乎所有 React 应用中最重要的部分,React 基于 state 和 props 渲染组件。每当它们发生变化时,组件就会重新渲染,并且 DOM 也会相应地更新。下面来看一个计数器的例子,它包含一个计数状态以及两个更新它的地方:

import { Component } from "react";
class ManagingStateClass extends Component {
  state = {
    counter: 0,
  };
  increment = () => {
    this.setState(prevState => {
      return {
        counter: prevState.counter + 1,
      };
    });
  };
  decrement = () => {
    this.setState(prevState => {
      return {
        counter: prevState.counter - 1,
      };
    });
  };
  render() {
    return (
      <div>
        <div>Count: {this.state.counter}</div>
        <div>
          <button onClick={this.increment}>Increment</button>
          <button onClick={this.decrement}>Decrement</button>
        </div>
      </div>
    );
  }
}
export default ManagingStateClass;

下面来使用 Hooks 实现这个计数器组件:


import { useState } from "react";
const ManagingStateHooks = () => {
  const [counter, setCounter] = useState(0);
  const increment = () => setCounter(counter => counter + 1);
  const decrement = () => setCounter(counter => counter - 1);
  return (
    <div>
      <div>Count: {counter}</div>
      <div>
        <button onClick={increment}>Increment</button>
        <button onClick={decrement}>Decrement</button>
      </div>
    </div>
  );
};
export default ManagingStateHooks;

该组件是一个返回 JSX 的函数,使用 useState hook来管理计算器的状态。它返回一个包含两个值的数组:第一个值为状态,第二个值为更新函数。并且使用 setCounter 来更新程序的incrementdecrement函数。


2. 状态更新后的操作

在某些情况下,我们可能需要在状态更新时执行某些操作。在类组件中,我们通常会在componentDidUpdate 生命周期中实现该操作。

import { Component } from "react";
class StateChangesClass extends Component {
  state = {
    counter: 0,
  };
  componentDidUpdate(prevProps, prevState) {
    localStorage.setItem("counter", this.state.counter);
  }
  increment = () => {
    this.setState(prevState => {
      return {
        counter: prevState.counter + 1,
      };
    });
  };
  decrement = () => {
    this.setState(prevState => {
      return {
        counter: prevState.counter - 1,
      };
    });
  };
  render() {
    return (
      <div>
        <div>Count: {this.state.counter}</div>
        <div>
          <button onClick={this.increment}>Increment</button>
          <button onClick={this.decrement}>Decrement</button>
        </div>
      </div>
    );
  }
}
export default StateChangesClass;

当状态发生变化时,我们将新的计数器值保存在 localStorage 中。在函数组件中,我们可以通过使用 useEffect hook 来实现相同的功能。

import { useState, useEffect } from "react";
const StateChangesHooks = () => {
  const [counter, setCounter] = useState(0);
  const increment = () => setCounter(counter => counter + 1);
  const decrement = () => setCounter(counter => counter - 1);
  useEffect(() => {
    localStorage.setItem("counter", counter);
  }, [counter]);
  return (
    <div>
      <div>Count: {counter}</div>
      <div>
        <button onClick={increment}>Increment</button>
        <button onClick={decrement}>Decrement</button>
      </div>
    </div>
  );
};
export default StateChangesHooks;

这个 useEffect hook 有两个参数,第一个参数是回调函数,第二个参数是依赖数组。在组件挂载时,这个 hook 至少会执行一次。然后,仅在依赖数组内的任何值发生变化时都会触发第一个参数传入的回调函数。如果依赖数组为空,则回调函数只会执行一次。在上面的例子中,每当 counter 发生变化时,都会触发将 counter 保存在 localStorage 中的回调函数。


3. 获取数据

在类组件中,通过会在componentDidMount生命周期中初始化一个 API 请求来获取数据。下面来看一个获取并显示帖子列表的组件:


import { Component } from "react";
class FetchingDataClass extends Component {
  state = {
    posts: [],
  };
  componentDidMount() {
    this.fetchPosts();
  }
  fetchPosts = async () => {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts");
    const data = await response.json();
    this.setState({
      posts: data.slice(0, 10),
    });
  };
  render() {
    return (
      <div>
        {this.state.posts.map(post => {
          return <div key={post.id}>{post.title}</div>;
        })}
      </div>
    );
  }
}
export default FetchingDataClass

有了 hooks,就可以使用useEffect来实现上述功能。它会在第一次挂载之后执行一次,然后在任何依赖发生变化时再次触发。useEffect 允许我们传入一个空依赖数组作为第二个参数来确保只执行一次effect的回调函数。

import { useState, useEffect } from "react";
const FetchingDataHooks = () => {
  const [posts, setPosts] = useState([]);
  const fetchPosts = async () => {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts");
    const data = await response.json();
    setPosts(data.slice(0, 10));
  };
  useEffect(() => {
    fetchPosts();
  }, []);
  return (
    <div>
      {posts.map(post => {
         return <div key={post.id}>{post.title}</div>;
      })}
    </div>
  );
};
export default FetchingDataHooks;


4. 卸载组件时清理副作用


在卸载组件时清理副作用是非常重要的,否则可能会导致内存泄露。例如,在一个组件中,我们想要监听一个事件,比如resize或者scroll,并根据窗口大小获滚动的位置来做一些事情。下面来看一个类组件的例子,它会监听 resize 事件,然后更新浏览器窗口的宽度和高度的状态。事件监听器在 componentWillUnmount 生命周期中被移除。


import { Component } from "react";
class CleanupClass extends Component {
  state = {
    width: window.innerWidth,
    height: window.innerHeight,
  };
  componentDidMount() {
    window.addEventListener("resize", this.updateWindowSize, {
      passive: true,
    });
  }
  componentWillUnmount() {
    window.removeEventListener("resize", this.updateWindowSize, {
      passive: true,
    });
  }
  updateWindowSize = () => {
    this.setState({
      width: window.innerWidth,
      height: window.innerHeight,
    });
  };
  render() {
    return (
      <div>
        Window: {this.state.width} x {this.state.height}
      </div>
    );
  }
}
export default CleanupClass;

useEffect 中,我们可以在回调函数中返回一个函数来执行清理操作,卸载组件时会调用此函数。下面,首先来定义一个 updateWindowSize 函数,然后在 useEffect 中添加 resize 事件监听器。 接下来返回一个匿名箭头函数,它将用来移除监听器。


import { useState, useEffect } from "react";
const CleanupHooks = () => {
  const [width, setWidth] = useState(window.innerWidth);
  const [height, setHeight] = useState(window.innerHeight);
  useEffect(() => {
    const updateWindowSize = () => {
      setWidth(window.innerWidth);
      setHeight(window.innerHeight);
    };
    window.addEventListener("resize", updateWindowSize, {
      passive: true,
    });
    return () => {
      window.removeEventListener("resize", this.updateWindowSize, {
        passive: true,
      });
    };
  }, []);
  return (
      <div>
        Window: {this.state.width} x {this.state.height}
      </div>
  );
};
export default CleanupHooks;


如何使用 React Hooks 重构类组件?(下)https://developer.aliyun.com/article/1411376

相关文章
|
1天前
|
移动开发 前端开发 JavaScript
React 视频播放控制组件 Video Controls
本文深入探讨了如何使用React创建功能丰富的视频播放控制组件(Video Controls)。首先介绍了React与HTML5 `&lt;video&gt;` 标签的基础知识,展示了如何通过状态管理和事件处理实现自定义控件。接着分析了常见问题如视频加载失败、控件样式不一致、状态管理混乱和性能问题,并提供了相应的解决方案。最后通过完整代码案例详细解释了播放、暂停、进度条和音量控制的实现方法,帮助开发者在React中构建高质量的视频播放组件。
36 17
|
6天前
|
移动开发 前端开发 UED
React 音频预览组件:Audio Preview
本文介绍如何使用 React 构建音频预览组件,涵盖基础实现、常见问题及解决方案。通过 HTML5 `&lt;audio&gt;` 标签和 React 状态管理,实现播放控制。解决文件路径、浏览器兼容性等问题,并优化性能,避免状态不同步和内存泄漏,提升用户体验。
54 22
|
8天前
|
移动开发 前端开发 UED
React 音频播放器组件 Audio Player
本文介绍如何使用React创建功能丰富的音频播放器组件。基于HTML5 `&lt;audio&gt;` 标签,结合React的状态管理和事件处理,实现播放、暂停、进度和音量控制等功能。通过代码示例展示基本实现,并探讨常见问题如自动播放限制、进度条不更新、文件加载失败及多实例冲突的解决方案。同时,避免易错点如忽略生命周期管理、错误处理和性能优化,确保高效开发与良好用户体验。
62 23
|
10天前
|
缓存 前端开发 JavaScript
React 视频弹幕组件 Video Danmaku
本文介绍了如何在React中构建视频弹幕组件,提升用户观看体验和互动性。首先通过Create React App初始化项目,并集成`react-player`作为视频播放器。接着实现基本弹幕功能,包括评论的接收与显示,使用CSS动画实现滚动效果。针对常见问题如弹幕重叠、性能下降及同步问题,提供了随机化位置、分批加载和监听播放进度等解决方案。最后探讨了弹幕分类和特效等高级技巧,确保弹幕系统的高性能和良好用户体验。
52 23
|
11天前
|
移动开发 前端开发 JavaScript
React 视频播放控制组件 Video Controls
本文介绍了如何使用 React 构建视频播放控制组件(Video Controls),涵盖基本概念、创建步骤和常见问题解决。首先,通过 HTML5 `&lt;video&gt;` 标签和 React 组件化思想,实现播放/暂停按钮和进度条等基础功能。接着,详细讲解了初始化项目、构建 `VideoControls` 组件及与主应用的集成方法。最后,针对视频无法播放、控制器状态不同步、进度条卡顿和音量控制失效等问题提供了具体解决方案,并介绍了全屏播放和自定义样式等进阶功能。希望这些内容能帮助你在实际项目中更好地实现和优化视频播放功能。
83 40
|
25天前
|
存储 编解码 前端开发
React 视频上传组件 Video Upload
随着互联网的发展,视频内容在网站和应用中愈发重要。本文探讨如何使用React构建高效、可靠的视频上传组件,涵盖基础概念、常见问题及解决方案。通过React的虚拟DOM和组件化开发模式,实现文件选择、进度显示、格式验证等功能,并解决跨域请求、并发上传等易错点。提供完整代码案例,确保用户能顺畅上传视频。
128 92
|
26天前
|
移动开发 前端开发 JavaScript
React 视频播放器组件:Video Player
本文介绍了如何使用 React 和 HTML5 `&lt;video&gt;` 标签构建自定义视频播放器组件。首先,通过创建基础的 React 项目和 VideoPlayer 组件,实现了基本的播放、暂停功能。接着,探讨了常见问题如视频加载失败、控制条样式不一致、性能优化不足及状态管理混乱,并提供了相应的解决方案。最后,总结了构建高效视频播放器的关键要点,帮助开发者应对实际开发中的挑战。
110 27
|
27天前
|
前端开发 UED 索引
React 图片灯箱组件 Image Lightbox
图片灯箱组件是一种常见的Web交互模式,用户点击缩略图后弹出全屏窗口展示大图,并提供导航功能。本文介绍了基于React框架的图片灯箱组件开发,涵盖初始化状态管理、图片加载与预加载、键盘和鼠标事件处理等常见问题及解决方案。通过`useState`和`useEffect`钩子管理状态,使用懒加载和预加载优化性能,确保流畅的用户体验。代码案例展示了组件的基本功能实现,包括打开/关闭灯箱、切换图片及键盘操作。
127 80
|
28天前
|
存储 前端开发 索引
React 图片轮播组件 Image Carousel
本文介绍了如何使用React创建图片轮播组件。首先,解释了图片轮播的基本概念和组件结构,包括图片容器、导航按钮、指示器和自动播放功能。接着,通过代码示例详细说明了创建基本组件、添加自动播放、处理边界情况的步骤。还探讨了常见问题如状态更新不及时、内存泄漏和样式问题,并提供了解决方案。最后,介绍了进阶优化,如添加过渡效果、支持触摸事件和动态加载图片,帮助读者构建更完善的轮播组件。
52 16
|
29天前
|
前端开发 JavaScript API
React 图片放大组件 Image Zoom
本文介绍如何使用React创建图片放大组件(Image Zoom),提升用户体验。组件通过鼠标悬停或点击触发放大效果,利用`useState`管理状态,CSS实现视觉效果。常见问题包括图片失真、性能下降和移动端支持,分别可通过高质量图片源、优化事件处理和添加触摸事件解决。易错点涉及状态管理混乱、样式冲突和过多事件绑定,建议使用上下文API、CSS模块及优化事件绑定逻辑。高级功能扩展如多张图片支持和自定义放大区域进一步丰富了组件的实用性。
58 25