一篇文章彻底搞懂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

相关推荐
williamdsy34 分钟前
【chrome 插件】初窥
前端·javascript·chrome·插件
ededabo1 小时前
正则表达式的使用规则
开发语言·前端·爬虫·python·正则表达式
我是一只鱼啊1 小时前
前端 OnePiece CSS (娜美)篇
前端·css
南風知意1 小时前
🏅el-tooltip 组件在全屏状态下不显示提示框问题
前端·css
qq_417371721 小时前
vue+天地图+Openlayers
前端·vue.js
闪光桐人2 小时前
uniapp/vue项目 import 导入文件时提示Module is not installed,‘@/views/xxx‘路径无法追踪
前端·vue.js·webpack·uni-app·vite
貂蝉空大2 小时前
uni-app 封装下拉选择组件 标红指定项
前端·javascript·uni-app
GoppViper2 小时前
uniapp js向json中增加另一个json的全部数据,并获取json长度
前端·javascript·前端框架·uni-app·json·uniapp
yzhSWJ2 小时前
使用npm link 把一个本地项目变成依赖,引入到另一个项目中
前端·npm·node.js
张志翔的博客2 小时前
2024最新国内镜像源设置(npm、yarn、pnpm)
前端·npm·node.js