一篇文章彻底搞懂React函数式组件

React函数组件 - 常用Hooks、组件间通信、高阶组件...

简介

很久很久之前,函数式组件由于没有this,故不能获取state,refs等,因此函数式组件不被看好。但是react16.8版本之后,推出了hooks,自此函数式组件崛起。本文从以下几点讲解

  • 函数组件传递props
  • Hooks是什么
  • 常用的Hooks
  • 高阶组件React.memo()
  • 新的组件间通信方式

Hooks是什么

通俗的说,Hook是React 16.8.0版本增加的新特性/新语法,可以让我们在函数组件中使用 state,refs 以及其他的 React 特性。

学习Hooks之前,我们必须要了解函数式组件是如何接收props的

函数式组件可以接收参数,它接受props作为参数。在组件内部,我们通过props来访问传递给组件的属性,如namemessage

以下是一个例子:

jsx 复制代码
import React from 'react';

// 定义一个函数式组件,接受props作为参数
function Test(props) {
  console.log(props)
  
  return (
    <div>
      <h1>Hello, {props.name}!</h1>
      <p>{props.message}</p>
    </div>
  );
}

// 使用组件并传递props
function App() {
  return (
    <div>
      <Test name="Alice" message="Welcome to learn React !" />
      <Test name="Bob" message="Have a great day!" />
    </div>
  );
}

export default App;

控制台输出以下内容:

掌握了最基本的props之后,我们继续学习Hooks的使用

常用的Hooks

1、React.useState()

useState()是一个非常重要的Hook,它用于在函数式组件中添加状态管理。以下是关于如何使用useState()的简要说明:

  • 导入useState :首先,确保在组件文件的顶部导入useState
  • 使用useState() :在函数式组件中,我们可以使用useState()来声明一个状态变量以及一个用于更新该状态的函数。通常,我们需要为状态变量提供一个初始值。
  • 访问状态值 :现在,我们可以在组件中访问count状态变量的值,就像访问任何其他JavaScript变量一样。
  • 更新状态 :要更新状态,使用setCount函数。这个函数会接收一个新的状态值,并触发组件的重新渲染。
jsx 复制代码
import React, { useState } from 'react';

function MyComponent() {
    const [count, setCount] = useState(0);
    conso.log(useState(0))
    
    return (
        <div>
            <p>Count: {count}</p>
        </div>
    );
}

以上示例展示了useState()的基本使用方式。我们可以在函数式组件中使用多个useState()来管理多个状态变量,每个状态变量都有自己的setState函数。这使得在React函数式组件中管理组件级别的状态变得非常容易。

为什么要用一个数组来接收React.useState()?

不着急,带着疑问出发,我们先输出一下React.useState()是什么东西

可以看到,输出的结果是一个数组,数组第一项是我们设置的初始值,第二项是个函数,用来改变我们设置的值。

useState()是同步的吗?

学过React类式组件的小伙伴都知道,类式组件中的this.setState()方法是异步的,那函数式组件的useState()是异步的吗?,先看以下一段代码

jsx 复制代码
import React, { useState } from 'react';

function MyComponent() {
    const [count, setCount] = useState(0);
    
    console.log('count值:',count)

    const increment = () => {
        setCount(count + 1);
        console.log('更新后的count值:',count); 
      };
    
    return (
        <div>
            <p>Count: {count}</p>
        </div>
    );
}

控制台输出结果:

可以看到,count值并没有立刻更新。是因为 setState 函数(这里是 setCount)是异步的操作。这意味着在调用 setCount 后,React 不会立即更新 count 的值,而是在稍后的某个时刻进行更新,这是为了优化性能。

如果想要在 count 更新后执行某些操作,可以使用 useEffect 钩子来监听 count 的变化,然后在变化发生时执行相应的操作。

2、React.useEffect()

useEffect(()=>{},[])可以接受两个参数,一个是函数,一个是依赖数组。它允许我们在函数式组件中执行一些操作,例如数据获取、订阅、手动DOM操作等。相当于类组件中的以下三个生命周期钩子函数:componentDidMount()componentDidUpdate()componentWillUnMount()

  • 模拟 componentDidMount()

useEffect(()=>{},[]),当useEffect的第二个参数是空数组时,只会在组件挂载后运行一次。

  • 模拟 componentDidUpdate()

useEffect(()=>{})或者useEffect(()=>{},[count]) 如果想要某个 state 值变化才触发函数,那么第二个参数的数组中必须加上这个 state 。如果只有一个参数,表示在组件挂载和更新时都会触发该函数,

  • 模拟 componentWillUnMount()
jsx 复制代码
useEffect(()=>{
    const timerId = setInterval(() => {
		// 做一些定时操作
	  }, 1000);
	
    return () => {
        clearInterval(timerId); // 清除定时器
    };
})

useEffect()的第一个参数是函数,它返回一个函数就相当于componentWillUnMount()。可以在返回的函数中执行清理操作,例如取消订阅、清除定时器等。

3、React.useMemo()

  • 用途useMemo 主要用于优化计算过程,它允许我们缓存并重用某个值,只有在指定的依赖项发生变化时才会重新计算。
  • 工作方式useMemo 接受两个参数,第一个是一个函数,用于计算需要缓存的值,第二个是一个依赖数组,包含影响计算结果的变量。当依赖数组中的变量发生变化时,useMemo 会重新计算并返回新的值。否则,它将返回上一次缓存的值。

useMemo()useEffect()区别:

  • useMemo 用于优化计算过程,返回一个缓存的值,只在依赖项变化时重新计算。
  • useEffect 用于处理副作用操作,包括在组件渲染后执行特定操作,并且可以在组件卸载前清理这些操作。它也可以通过依赖数组控制何时运行副作用。
  • useMemo 通常返回一个值,而 useEffect 返回一个清理函数,用于在组件卸载前执行清理操作。

4、React.useRef()

React.useRef() 主要用于创建可变的 Ref 对象。Ref 对象可以用于访问 DOM 元素、存储组件实例引用或在函数式组件中保存可变数据。React.useRef() 用法如下:

  1. 创建 Ref 对象

可以使用 React.useRef() 来创建一个 Ref 对象,如下所示:

jsx 复制代码
const myRef = React.useRef();

这将创建一个名为 myRef 的 Ref 对象。

  1. 访问 DOM 元素

useRef() 最常见的用法是用于访问 DOM 元素。你可以将 ref 属性设置为一个 Ref 对象,以引用 DOM 元素。例如:

jsx 复制代码
function MyComponent() {
  const myRef = React.useRef();

  useEffect(() => {
    // 访问 DOM 元素
    myRef.current.focus();
  }, []);

  return <input ref={myRef} />;
}

在上面的示例中,myRef 被用来引用 <input> 元素,并在组件挂载后将焦点设置在该输入框上。想要了解更多有关React.useRef() 的小伙伴可以查看官方文档

高阶组件 React.memo()

React.memo()不同于以上Hooks,是一个高阶组件(Higher Order Component,HOC) ,用于优化函数式组件的渲染性能。它的主要作用是在组件接收的 props 没有发生变化时,阻止组件的不必要重新渲染。

jsx 复制代码
import React from 'react';

function MyComponent(props) {
  // 组件渲染逻辑
}

// 使用 React.memo 包装组件以进行性能优化
const MemoizedComponent = React.memo(MyComponent);

当我们使用 React.memo() 包装组件后,它会将组件的 props 与前一次渲染的 props 进行比较。只有当 props 发生变化时,组件才会重新渲染。这有助于避免不必要的渲染操作,提高应用程序的性能。

组件间通信方式 ------ Context

Context 是 React 中一种用于跨组件传递数据的机制,既可以用于类组件,也可以用于函数式组件。它允许我们在组件树中的多个层次之间共享数据,而无需手动将数据通过 props 一层一层地传递。Context 主要包含两个部分:ProviderConsumer

1.Provider

Provider 是用于提供共享数据的组件,它将数据传递给它的子组件。在创建 Context 时,你需要使用 React.createContext() 创建一个 Context 对象,并在 Provider 中设置要共享的数据。

jsx 复制代码
// 创建一个 Context 对象
export const MyContext = React.createContext();
jsx 复制代码
import React ,{useContext} from 'react'
import { MyContext } from './MyContext';

function MyComponent() {
  // 使用 Provider 提供数据
  return (
    <MyContext.Provider value={someData}>
         <MyChildComponent/>
    </MyContext.Provider>
  );
}

2. Consumer:

Consumer 是用于访问共享数据的组件。它可以在任何深度的子组件中使用,以获取提供者传递的数据。

函数式组件中:

在函数式组件中,通常可以使用 useContextMyContext.Consumer 来访问数据。

1.使用 MyContext.Consumer

jsx 复制代码
import React from 'react';
import { MyContext } from './MyContext';

function MyChildComponent() {
  return (
    <MyContext.Consumer>
      {data => <div>{data}</div>}
    </MyContext.Consumer>
  );
}

2.使用 useContext()

jsx 复制代码
import React, { useContext } from 'react';
import { MyContext } from './MyContext';

function MyChildComponent() {
  // 使用 useContext Hook 访问共享数据
  const data = useContext(MyContext);
  return <div>{data}</div>;
}

类组件中:

虽然在类组件中使用 Context.Consumer 是有效的,但在新的 React 16.3 版本及更高版本中,推荐使用静态属性 contextTypeConsumer 高阶组件(HOC)的方式来更方便地访问 Context

1.使用contextType

jsx 复制代码
import React from "react";
import { MyContext} from "./MyContext";

class ChildClassComponent extends React.Component {
    static contextType = MyContext; 

    componentDidMount() {
        const value = this.context; 
        console.log("Value from MyContext:", value);
    }

    render() {
        return (
              <p>{this.context}</p>
          );
    }
}

注意: 静态属性contextType不可以随意更改 是 React 所识别的属性名称,它告诉 React 将 MyContext 中的数据赋值给 this.context,以便在组件中访问。

我想获取多个不同的MyContext怎么办?

jsx 复制代码
// 设置第一个 Context:
static contextType = MyContext; 

const value1 = this.context; // 访问第一个 Context

//设置第二个 Context:
static anotherContextType = TestContext 

const value2 = this.constructor.anotherContextType._currentValue; // 访问第二个 Context

2.封装Consumer 高阶组件(HOC)

jsx 复制代码
const withMyContext = WrappedComponent => {
	return props => (
		<MyContext.Consumer>
			{value => <WrappedComponent {...props} context={value} />}
		</MyContext.Consumer>
	);
};

Context 适用情况

  1. 当我们需要在组件树的多个层次中传递相同的数据时,而不想通过一层一层的 props 传递。
  2. 当我们有一个全局状态(例如用户身份验证、主题设置等)需要在多个组件之间共享时。

请注意,使用 Context 应谨慎。过度使用 Context 可能会导致组件之间的紧密耦合,使代码难以理解和维护。通常情况下,应该优先考虑将共享数据提升到更接近需要使用它的组件的层次,以保持组件的可预测性和可维护性。

若想深入了解Context相关知识的小伙伴,可以参考官方文档useContextcreateContext

相关推荐
恋猫de小郭1 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端