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
来访问传递给组件的属性,如name
和message
。
以下是一个例子:
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()
用法如下:
- 创建 Ref 对象:
可以使用 React.useRef()
来创建一个 Ref 对象,如下所示:
jsx
const myRef = React.useRef();
这将创建一个名为 myRef
的 Ref 对象。
- 访问 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
主要包含两个部分:Provider
和 Consumer
。
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
是用于访问共享数据的组件。它可以在任何深度的子组件中使用,以获取提供者传递的数据。
函数式组件中:
在函数式组件中,通常可以使用 useContext
或 MyContext.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 版本及更高版本中,推荐使用静态属性 contextType
或 Consumer
高阶组件(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 适用情况
- 当我们需要在组件树的多个层次中传递相同的数据时,而不想通过一层一层的 props 传递。
- 当我们有一个全局状态(例如用户身份验证、主题设置等)需要在多个组件之间共享时。
请注意,使用 Context
应谨慎。过度使用 Context
可能会导致组件之间的紧密耦合,使代码难以理解和维护。通常情况下,应该优先考虑将共享数据提升到更接近需要使用它的组件的层次,以保持组件的可预测性和可维护性。
若想深入了解Context相关知识的小伙伴,可以参考官方文档useContext,createContext