您现在的位置是:网站首页> 编程资料编程资料

使用 React Hooks 重构类组件的示例详解_React_

2023-05-24 424人已围观

简介 使用 React Hooks 重构类组件的示例详解_React_

最初,在 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 ( 
Count: {this.state.counter}
); } } 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 ( 
Count: {counter}
); }; export default ManagingStateHooks;

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

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 ( 
Count: {this.state.counter}
); } } 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 ( 
Count: {counter}
); }; 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 ( 
{this.state.posts.map(post => { return
{post.title}
; })}
); } } 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 ( 
{posts.map(post => { return
{post.title}
; })}
); }; 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 ( 
Window: {this.state.width} x {this.state.height}
); } } 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 ( 
Window: {this.state.width} x {this.state.height}
); }; export default CleanupHooks;

5.  防止组件重新渲染

React 非常快,通常我们不必担心过早的优化。但是,在某些情况下,优化组件并确保它们不会过于频繁地重新渲染是很有必要的。

例如,减少类组件重新渲染的常用方法是使用 PureComponent​ 或者 shouldComponentUpdate​ 生命周期。下面例子中有两个类组件(父组件和子组件),父组件有两个状态值:counter​ 和 fruit​。子组件只在父组件的 fruit​ 发生变化时重新渲染。所以,使用 shouldComponentUpdat​e 生命周期来检查 fruit 属性是否改变。如果相同,则子组件不会重新渲染。

父组件:

import { Component } from "react"; import PreventRerenderClass from "./PreventRerenderClass.jsx"; function randomInteger(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } const fruits = ["banana", "orange", "apple", "kiwi", "mango"]; class PreventRerenderExample extends Component { state = { fruit: null, counter: 0, }; pickFruit = () => { const fruitIdx = randomInteger(0, fruits.length - 1); const nextFruit = fruits[fruitIdx]; this.setState({ fruit: nextFruit, }); }; componentDidMount() { this.pickFruit(); } render() { return ( 

Current fruit: {this.state.fruit} | counter: {this.state.counter}

); } } export default PreventRerenderExample;

子组件:

import { Component } from "react"; class PreventRerenderClass extends Component { shouldComponentUpdate(nextProps, nextState) { return this.props.fruit !== nextProps.fruit; } render() { return ( 

Fruit: {this.props.fruit}

); } } export default PreventRerenderClass;

随着 hooks 的引入,我们得到了一个新的高阶组件,称为 memo。它可用于优化性能并防止函数组件重新渲染。下面来看看它是怎么用的。

父组件:

import { useState, useEffect } from "react"; import PreventRerenderHooks from "./PreventRerenderHooks.jsx"; function randomInteger(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } const fruits = ["banana", "orange", "apple", "kiwi", "mango"]; const PreventRerenderExample = () => { const [fruit, setFruit] = useState(null); const [counter, setCounter] = useState(0); const pickFruit = () => { const fruitIdx = randomInteger(0, fruits.length - 1); const nextFruit = fruits[fruitIdx]; setFruit(nextFruit); }; useEffect(() => { pickFruit(); }, []); return ( 

Current fruit: {fruit} | counter: {counter}

); }; export default PreventRerenderExample;

子组件:

import { memo } from "react"; const PreventRerenderHooks = props => { return ( 

Fruit: {props.fruit}

); }; export default memo(PreventRerenderHooks);
提示: 本文由整理自网络,如有侵权请联系本站删除!
本站声明:
1、本站所有资源均来源于互联网,不保证100%完整、不提供任何技术支持;
2、本站所发布的文章以及附件仅限用于学习和研究目的;不得将用于商业或者非法用途;否则由此产生的法律后果,本站概不负责!

-六神源码网