赞
踩
React创建组件的方式有三种,分别是函数式组件、类组件,还有createElement组件。react v16.8版本之前函数式组件是没有状态的。但是,自16.8以后得版本有个hook函数,函数式组件也有了状态,反而类组件没有多少人写了,原因在于生命周期很麻烦,也难记。笔者近几年写React项目已经很少使用类组件了。接下来,详细探讨下hook函数,为什么会有那么神奇的效果?
React Hooks 是 React v16.8 之后推出的函数式组件状态管理方案。它是为了解决状态复用、类组件写法麻烦等原因而提出的,Hook函数本质是闭包。Hooks 主要是利用闭包来保存状态,使用链表保存一系列 Hooks,将链表中的第一个 Hook 与 Fiber 关联。在 Fiber 树更新时,就能从 Hooks 中计算出最终输出的状态和执行相关的副作用。Hook函数带来便利有逻辑复用、业务代码更聚合、写法更简洁。
常用的Hook函数useState、useEffect、useRef、useCallback、useMemo、useReducer、useLayoutEffect
useState是React自带的一个Hook函数,使用useState可声明内部状态变量。useState接收的参数为状态初始值或状态初始化方法,它返回一个数组。数组的第一项是当前状态值,每次渲染其状态值可能都会不同;第二项是可改变对应状态值的set函数,在useState初始化后该函数不会变化。
useState的类型为:
function useState<S>(initialState:S|(() => S )): [S,Dispatch <SetStateAction <S>>];
initialState仅在组件初始化时生效,后续的渲染将忽略initialState:
- const [value, setValue] = useState("");
- const [count, setCount] = useState(value);
如上例中的value,当初始值传入另一个状态并初始化后,另一个状态函数将不再依赖value的值。
在 class 中,我们需要调用 this.setState()
来更新 count
值,在函数式组件中,我们已经有了 setCount
和 count
变量,所以我们不需要 this:
- import {useState} from "react";
- const Example = () => {
- const [count, setCount] = useState(0);
- return (
- <div>
- <button onClick={() => {setCount(count+1)}}>
- 点击更新count:{count}
- </button>
- </div>
- )
- }
类似于setState,单击按钮时调用setCount更新了状态值count。当调用setCount后,组件会重新渲染,count的值会得到更新。
当传入初始状态为函数时,其仅执行一次,类似于类组件中的构造函数:
useState返回的更新函数也可使用函数式更新:
setCount(preCount => preCount + 1)
如果新的state需要依赖先前的 state 计算得出,那么可以将回调函数当作参数传递给setState。该回调函数将接收先前的state,并将返回的值作为新的state进行更新。
在组件生命周期或React合成事件中,setState是异步;在setTimeout或者原生dom事件中,setState是同步。
为什么react大部分情况setState是异步的呢?假如所有setState是同步的,意味着每执行一次setState时(有可能一个同步代码中,多次setState),都重新vnode diff + dom修改,这对性能来说是极为不好的。如果是异步,则可以把一个同步代码中的多个setState合并成一次组件更新。
useEffect函数会在组件渲染完毕后,执行和渲染无关的副作用,如数据获取,设置订阅以及手动更改 React 组件中的 DOM 等。
有了useEffect,我们可以在函数组件中实现 像类组件中的生命周期那样某个阶段做某件事情,具有:
基本用法:
- useEffect(() => {
- console.log('这是一个不含依赖数组的useEffect,每次render都会执行!')
- })
useEffect接受一个回调函数和一个可选的依赖项数组。回调函数会在组件挂载、更新和卸载时执行,根据依赖项的不同情况选择执行。如果依赖项为空数组,那么回调函数只会在组件挂载和卸载时执行一次。如果依赖项中包含某个状态或属性,那么只有在这个状态或属性发生变化时才会执行回调函数。
useEffect的规则
下面是useEffect的一些常见用法:
1、获取数据和执行网络请求:
- import React, { useState, useEffect } from 'react';
- import axios from 'axios';
-
- function MyComponent() {
- const [data, setData] = useState([]);
-
- useEffect(() => {
- const fetchData = async () => {
- const result = await axios.get('/api/data');
- setData(result.data);
- }
- fetchData();
- }, []);
-
- return (
- <div>
- {data.map((item) => <div key={item.id}>{item.name}</div>)}
- </div>
- );
- }
-
- export default MyComponent;
2、订阅事件:
- import { useEffect } from 'react';
-
- function MyComponent() {
- useEffect(() => {
- const subscription = myEventEmitter.subscribe('event', () => {
- // 处理事件逻辑
- });
-
- return () => {
- subscription.unsubscribe();
- };
- }, []);
-
- return <div>My Component</div>;
- }
3、执行清理操作:
- import { useEffect, useState } from 'react';
-
- function MyComponent() {
- const [timer, setTimer] = useState(null);
-
- useEffect(() => {
- const id = setInterval(() => {
- // 处理定时器逻辑
- }, 1000);
-
- setTimer(id);
-
- return () => {
- clearInterval(timer);
- };
- }, []);
-
- return <div>My Component</div>;
- }
4、使用第三方库:
- import { useEffect } from 'react';
- import moment from 'moment';
-
- function MyComponent() {
- useEffect(() => {
- moment.locale('zh-cn');
- }, []);
-
- return <div>{moment().format('LLLL')}</div>;
- }
需要注意的是,useEffect回调函数中的操作可能会对应用程序的性能产生影响。如果回调函数执行的操作很耗费资源,那么可能会导致应用程序变慢。因此,需要谨慎使用useEffect,并在需要时进行性能优化。
在使用React Hooks时,需要遵守以下准则及特性要求。
只在顶层使用Hooks。不要在循环、条件或嵌套函数中调用Hooks,确保总是在React函数组件的顶层调用它们。
不要在普通的JavaScript函数中调用Hooks。仅在React的函数组件中调用Hooks,以及在自定义Hook中调用其他Hooks。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。