一篇文章带你了解react中的常用hooks

Hooks的概念

        Hooks是React 16.8引入的特性,允许在函数组件中使用状态(state)和其他React特性(如生命周期、上下文等),而无需编写class组件。Hooks的目的是解决函数组件功能不足的问题,同时简化代码逻辑,提高复用性。

        在Hooks之前,函数组件被称为“无状态组件”,仅能接收props并返回UI,无法管理内部的状态、使用生命周期方法或访问React的高级功能(如ref、上下文等)。复杂逻辑必须依赖class组件实现,导致代码逻辑分散。

        以下我通过一个例子让大家了解hooks出来前的class组件和函数组件

clss组件:

import { Component } from 'react';

class App extends Component {
    constructor(props) {
        super(props);

        this.state = {
            count: 0
        };
    }
    render() {
        const { count } = this.state;
        return (
            <>
                <div>{count}</div>
                <button onClick={() => {
                    this.setState(prevState => ({
                        count: prevState.count + 1
                    }));
                }}>点击</button>
            </>
        );
    }
}

export default App;

 函数组件:

function App() {
    let count = 0;
    return (
        <>
            <div>{count}</div>
            <button type="button" onClick={() => {
                count ++;
                console.log(count);
            }}>+1</button>
        </>
    )
}

export default App;

打印结果:

        不管是class组件还是函数组件,它们实现的都是点击按钮,数据+1,但是为什么使用函数组件他控制台打印的数据自增了,但页面上面还是0。

        这是因为声明的不是一个状态,只是普通的变量,在react中状态发生改变,组件会重新渲染,那如何在函数组件中声明一个状态,接下来到主题了。

 常用Hooks及其作用

useState

        允许函数组件管理内部状态。

        需要传入一个初始值,它返回一个数组,数组中有两个元素,第一个是当前的值,第二个是一个函数,用来设置这个值的。

        修改刚刚的例子:

function App() {
    const [count, setCount] = useState(0)
    return (
        <>
            <div>{count}</div>
            <button type="button" onClick={() => {
                setCount(count+1)
            }}>+1</button>
        </>
    )
}

        这时候count就是一个状态了,然后使用setCount这个函数来改变count的值。可以看到使用函数组件+hooks使得代码更加简洁和明了。

useEffect

        在官网的解释中它允许你将组件与外部系统同步,例如,你可能希望根据 React state 控制非 React 组件、建立服务器连接或当组件在页面显示时发送分析日志。Effect 允许你在渲染结束后执行一些代码,以便将组件与 React 外部的某个系统相同步。

要编写一个Effect,请遵循以下三个步骤:

1.声明Effect。通常Effect会在每次提交后运行。

2.指定Effect依赖。大多数Effect需要按需运行,而不是每次渲染后都运行。

3.必要时添加清理操作。一些Effect需要指定如何停止、撤销,或者清除它们所执行的操作。

import { useState, useEffect } from "react";

function App() {
    const [count, setCount] = useState(0)
    useEffect(()=>{
        // 每次渲染后都会执行此处代码
        console.log('hello world');
    })
    return (
        <>
            <div>{count}</div>
            <button type="button" onClick={() => {
                setCount(count+1)
            }}>+1</button>
        </>
    )
}

export default App;

        每点击按钮一次,useEffect都会被调用,也就是说每当你的组件渲染时,React会先更新页面,然后再运行useEffect中的代码。换句话说,useEffect会“延迟”一段代码的运行,直到渲染结果反馈在页面上。

        但是有时候不希望每次组件渲染都运行useEffect中的代码都会执行,就比如以下代码:

import { useState, useEffect } from "react";

function App() {
    const [count, setCount] = useState(0)
    const [text, setText] = useState('')
    useEffect(() => {
        // 每次渲染后都会执行此处代码
        console.log('hello world');
    })
    return (
        <>
            <input type="text" value={text} onChange={e => {setText(e.target.value)}}/>
            <div>{count}</div>
            <button type="button" onClick={() => {
                setCount(count + 1)
            }}>+1</button>
        </>
    )
}

export default App;

        在上述代码中,不管是在输入框中输入内容还是点击按钮,组件都会渲染,useEffect代码也会执行,但有时候这不是我们期望的结果,就比如我只希望点击按钮时才会执行useEffect中的代码,则需要给useEffect添加依赖项。

        通常在调用useEffect时指定一个依赖项作为第二个参数,可以让React跳过不必要地重新运行Effect

import { useState, useEffect } from "react";

function App() {
    const [count, setCount] = useState(0)
    const [text, setText] = useState('')
    useEffect(() => {
        // 每次渲染后都会执行此处代码
        console.log('hello world');
    }, [count]) // 这里的代码不但会在组件挂载时运行,而且当count的值自上次渲染后发生变化后也会运行
    return (
        <>
            <input type="text" value={text} onChange={e => { setText(e.target.value) }} />
            <div>{count}</div>
            <button type="button" onClick={() => {
                setCount(count + 1)
            }}>+1</button>
        </>
    )
}

export default App;

        这样除了useEffect中的代码除了挂载时运行,而且在count的值发生变化后也会运行。现在在表单中输入内容后不会运行了。

        当然也可以不传依赖项,只写一个空数组,这样代码只会在组件挂载时运行。

useEffect(() => {
  // 这里的代码会在每次渲染后运行
});

useEffect(() => {
  // 这里的代码只会在组件挂载(首次出现)时运行
}, []);

useEffect(() => {
  // 这里的代码不但会在组件挂载时运行,而且当 a 或 b 的值自上次渲染后发生变化后也会运行
}, [a, b]);

        到这里其实大家都有一个疑问,为什么Effect在挂载时运行了两次,这是因为在开发环境下,如果开启严格模式,React会在实际运行setup之前额外运行一次setup和cleanup。如果只需要调用一次,则关闭严格模式即可。

// import { StrictMode } from 'react' // 严格模式
import { createRoot } from 'react-dom/client'
import App from './components/text'

createRoot(document.getElementById('root')).render(
  // <StrictMode>
    <App></App>
  // </StrictMode>,
)

useContext

        在学习useContext之前先来写一个组件数据共享的例子,假如需要将父组件中的状态传给子组件,需要在父组件中的子组件标签中添加两个属性,一个是传递状态,另外一个是传递改变状态的方法,在子组件通过参数props接收传递过来的状态和方法,如下收拾:

根组件:

import { useState } from "react"
import Parent from "./Parent"

function MyApp() {
    const [count, setCount] = useState(0)
    return (
        <>
            <h1>根组件 -{count}</h1>
            <Parent count={count} setCount={setCount}></Parent>
        </>
    )
}

export default MyApp

父组件:

import Child from "./Child"

function Parent(props) {
    // eslint-disable-next-line react/prop-types
    const { count, setCount } = props
    return (
        <>
            <h2>父组件 -{count}</h2>
            <Child count={count} setCount={setCount}></Child>
        </>
    )
}

export default Parent

子组件:

function Child(props) {
    // eslint-disable-next-line react/prop-types
    const { count, setCount } = props
    return (
        <>
            <h3>子组件 -{count}</h3>
            <button type="button" onClick={() => { setCount(count + 1) }}>+1</button>
        </>
    )
}

export default Child

效果展示:

        当点击按钮后可以看到每个组件中的count状态都发生了改变,说明我在多个组件中确实是共享了数据,但是这种方式有些麻烦,假设底下还有组件,不可能接着往下传,跟套娃一样,不仅仅代码量比较大,更难以维护,因此react官方推出了useContext这个hooks,下面就来使用useContext同样来实现这个数据共享的能力。

        首先,useCountext的意思的使用上下文,在使用上下文之前需要创建上下文,使用到了createContext来创建上下文。

创建Context实例

        新建一个单独的文件用于创建Context实例,并导出该实例:

import { createContext } from "react";

// 创建环境对象
const MyContext = createContext()

export default MyContext

在根组件中提供Context值

        在应用的根组件中使用Provider传递需要共享的状态:

import { useState } from "react"
import MyContext from "./MyContext"
import Parent from "./Parent"

function MyApp() {
    const [count, setCount] = useState(0)
    return (
        <>
            {/* value里面传的值,在里面的子组件以及子组件的子组件都能获取到 */}
            <MyContext.Provider value={{ count, setCount }}>
                <h1>根组件 -{count}</h1>
                <Parent count={count} setCount={setCount}></Parent>
            </MyContext.Provider>
        </>
    )
}

export default MyApp

在子组件中消费Context

        任何层级的子组件都可以直接使用useContext获取共享状态:

import { useContext } from "react"
import MyContext from "./MyContext"

function Child() {
    const { count, setCount } = useContext(MyContext)
    return (
        <>
            <h3>子组件 -{count}</h3>
            <button type="button" onClick={() => { setCount(count + 1) }}>+1</button>
        </>
    )
}

export default Child

        这种模式避免了组件props的层层传递,使状态管理更加简洁高效。Context的Provider和Consumer机制实现了跨组件层级的数据共享。

结语:

        React Hooks 提供的高效开发方式已成为现代前端开发的核心工具。本次重点介绍的 Hooks 功能虽然只是整体的一部分,但它们在实际项目中应用频率最高,能够显著提升代码的可维护性和开发效率。

        掌握这些常用 Hooks 后,可以逐步探索更高级的用法,进一步优化组件逻辑和状态管理。熟练运用这些基础功能将为后续深入 React 生态打下坚实基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值