如何使用 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

相关文章
|
9天前
|
前端开发 测试技术 开发工具
探索前端框架React Hooks的优势与应用
本文将深入探讨前端框架React Hooks的优势与应用。通过分析React Hooks的特性以及实际应用案例,帮助读者更好地理解和运用这一现代化的前端开发工具。
|
2月前
|
前端开发 JavaScript UED
使用React Hooks优化前端应用性能
本文将深入探讨如何使用React Hooks来优化前端应用的性能,重点介绍Hooks在状态管理、副作用处理和组件逻辑复用方面的应用。通过本文的指导,读者将了解到如何利用React Hooks提升前端应用的响应速度和用户体验。
|
8天前
|
前端开发
探索React Hooks:一种全新的组件逻辑管理方式
React Hooks是React 16.8版本引入的一项新功能,它改变了我们编写React组件的方式。本文将从Hooks的起源讲起,逐步分析Hooks的优势,并通过具体示例展示Hooks在组件逻辑管理中的应用,旨在帮助读者更好地理解和运用React Hooks。
|
20天前
|
前端开发 JavaScript
使用React Hooks实现简单的计数器应用
使用React Hooks实现简单的计数器应用
|
20天前
|
前端开发 JavaScript
【边做边学】React Hooks (二)——useEffect Hook
【边做边学】React Hooks (二)——useEffect Hook
|
20天前
|
前端开发
【边做边学】系统解读一下React Hooks
【边做边学】系统解读一下React Hooks
|
2月前
|
存储 缓存 前端开发
【React】Hooks面试题集锦
本文集合一些React的Hooks面试题,方便读者以后面试查漏补缺。作者给出自认为可以让面试官满意的简易答案,如果想要了解更深刻,可以点击链接查看对应的详细博文。在此对链接中的博文作者非常感谢🙏。
53 1
|
2月前
|
前端开发
利用React Hooks优化前端状态管理
本文将深入介绍如何利用React Hooks优化前端状态管理,包括Hooks的概念、使用方法以及与传统状态管理方式的对比分析,帮助前端开发人员更好地理解和应用这一现代化的状态管理方案。
|
2月前
|
前端开发 开发者
React Hooks:提升前端开发效率和代码可维护性
传统的 React 类组件在处理状态管理和生命周期函数时存在一些限制,而引入的 React Hooks 技术可以帮助前端开发者更高效地管理组件状态和逻辑,提升代码的可读性和可维护性。本文将介绍 React Hooks 的基本用法及其在实际项目中的应用场景。
|
3月前
|
前端开发 JavaScript 测试技术
React Hooks之useState、useRef
React Hooks之useState、useRef