R

React Hooks 初学者指南

2025-05-24

React Hooks 初学者指南

什么是 Hooks

React hooks 就像是 React 状态(海底)和功能组件的生命周期特性(船)之间的锚(就像船在海里扔下的东西,用来连接船和海底)。

  • 仅适用于基于功能的组件,不适用于基于类的组件。
  • 箭头和常规函数组件均可用
  • 不能在循环、条件或嵌套函数内嵌套钩子

useState()

useStatehook 为你提供了设置变量状态的功能,并使用新状态自动更新 DOM

设置状态

如何导入:

import React, {useState} from "react";
// or 
React.useState;
Enter fullscreen mode Exit fullscreen mode

例子useState

import React, { useState } from 'react'

let count1 = 0;

function App() {

  const [count, setCount] = useState(0);

  let count2 = 0;

  return (
    <div className='container mt-3'>
      <h3>Hello World to React Hooks</h3>

      <h4>Count : {count}</h4>
      <h4>Count1 : {count1}</h4>
      <h4>Count2 : {count2}</h4>

      <button className='btn btn-info' onClick={() => {
        setCount(count + 1);

        console.log(`count : ${count} | count1 : ${count1}  count2 :${count2}`);

        count1 = count1 + 1;
        count2 = count2 + 1;
      }} >Add here</button>
    </div>
  )
}

export default App
Enter fullscreen mode Exit fullscreen mode

react_setState

在此代码片段中,countcount1都将作为变量更新,并且 中的 也会更新DOM。但count2始终为 1(因为 中的 +1 操作button.onClick),因为每当 React 组件中的任何数据发生变化时,整个组件都会重新渲染。这就是组件存在的原因。

现在你可能会问,我们可以在全局状态中声明变量而不使用useState。除了某些情况外,在所有编程语言中声明全局变量都被认为是不好的做法。参考:

useState即使组件重新渲染,也能提供一致的状态。

useState对于对象

import React, { useState } from 'react'

function App() {

  const [{ counter1, counter2 }, setCounter] = useState({ counter1: 0, counter2: 20 })

  return (
    <div className='container mt-3'>
      <div className='container'>

        <h3>Counter1 : {counter1}</h3>
        <h3>Counter2 : {counter2}</h3>

{/* this doesnt not work becuz whenever you update state, you need to update the whole object */}
{/* Over here, we havent included the counter2 in the setCounter function. */}

        <button className="btn btn-primary" onClick={() =>
          setCounter(currentState => ({ counter1: currentState.counter1 + 1 }))}>Add</button> &nbsp;

{/* this will also not work because spread operator in objects comes first 
    unlike in functions, where spread operator comes last. */}

{/* Correct Code */}
                <button className="btn btn-danger" onClick={() => setCounter(currentState => ({
          ...currentState,          
          counter1: currentState.counter1 - 1,
        }))}>Subtract</button

      </div>
    </div>
  )
}

export default App;
Enter fullscreen mode Exit fullscreen mode

另一个例子useState()

import React, { useState } from "react";

function App() {

  const [name, setName] = useState(localStorage.getItem("name") || "");

  return (
    <div className="App">

      <div className="container mt-3">
        <input name="name" value={name} onChange={e => {
          setName(e.target.value)
          localStorage.setItem("name", e.target.value)
        }} className='form-control' />

        <h3>Name : {name}</h3>
      </div>

    </div >
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

useEffect()

  • 每次渲染组件时执行
  • useEffect当传递时,没有依赖项起作用componentDidMount
  • 返回箭头函数useEffect是一个清理函数
  • 多个useEffecthook 可以共存于一个组件中
import React, { useState, useEffect } from "react";
import HelloWorld from "./component/HelloWorld";

function App() {

  const [count, setCount] = useState(0);
  const [showComp, setComp] = useState(false);

  useEffect(() => {
    console.log("Rendering ...")
  })

  return (
    <div className="App">

      <br />

      <div className='container mt-3'>
        <h3>Count : {count}</h3>
        <button className="btn btn-primary" onClick={() => setCount(count + 1)}>Add</button> &nbsp;
      </div>

      <br />

      <div className='container'>
        <button onClick={() => setComp(!showComp)} className="btn btn-info"> toggle </button>
        {showComp && <HelloWorld />}
      </div>

    </div >
  );
}

export default App;

// in src/component/HelloWorld.jsx

import React from 'react'

function HelloWorld() {
    return (
        <div className='container mt-3'>
            <h2>HelloWorld component</h2>
        </div>
    )
}

export default HelloWorld
Enter fullscreen mode Exit fullscreen mode

useEffect

运行代码并查看控制台...无论您增加计数器还是切换组件,整个组件都会重新渲染。

要停止此操作,useEffect请按如下方式修改

useEffect(() => {
    console.log("Rendering ...")
}, [])
Enter fullscreen mode Exit fullscreen mode

现在,只有刷新页面时,渲染才会打印到控制台上。请尝试修改代码,如下所示

useEffect(() => {
    console.log("Rendering ...")
}, [count])
Enter fullscreen mode Exit fullscreen mode

现在仅当计数更新时组件才会重新渲染

这就是我们的useEffect做法,只在需要时更新/渲染组件。还有一种方法可以清理组件。尝试修改HelloWorld.jsx

import React from 'react'

function HelloWorld() {

    React.useEffect(() => {
        console.log('HelloWorld')
        return () => {
            console.log('GoodByeWorld')
        }
    }, [])

    return (
        <div className='container mt-3'>
            <h2>HelloWorld component</h2>
        </div>
    )
}

export default HelloWorld

// and App.jsx

useEffect(() => {
    console.log("Rendering ...")
},[])
Enter fullscreen mode Exit fullscreen mode

现在尝试切换开关,您将看到组件已加载的消息DOM以及其加载时间。其工作原理unmounting类似于componentWillMountcomponentWillUnmount

useRef

当你只是想把一些 html 元素或 React 组件放在焦点时

最好尝试运行此代码

import React, { useRef } from "react";

function App() {

  const inputRef = useRef();

  return (
    <div className="App">

      <div className="container mt-3">

        <input ref={inputRef} name="name" value={name} onChange={e => {
          setName(e.target.value)
          localStorage.setItem("name", e.target.value)
        }}
          className='form-control'
        />

        <br />

        <button onClick={() => {
          inputRef.current.focus();
        }} className="btn btn-success" >Get focus</button>

      </div>

    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

使用参考

useReducer

图表最好地解释了这个钩子

用户Reducer

import React, { useReducer } from "react";

function reducer(state, action) {
  switch (action.type) {
    case 'increment': return state + 1;
    case 'decrement': return state - 1;
    default: return state;
  }
}

function App() {

  const [count, dispatch] = useReducer(reducer, 0);

  return (
    <div className="App">

      <div className='container' >
        <h2> count : {count} </h2>
        <button onClick={() => dispatch({ type: 'increment' })} className='btn btn-primary' > increment </button>
        <button onClick={() => dispatch({ type: 'decrement' })} className='btn btn-danger' > increment </button>

      </div>

    </div >
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

useContext

图表最好地解释了这个钩子

useContext

// App.js
import React from 'react'
import HelloWorld from "./components/HelloWorld"
import About from './component/About'
import { UserContext } from './UserContext'

function App() {
  return (
    <div>
      <UserContext.Provider value='super man'>
        <HelloWorld />
      </UserContext.Provider>
    </div>
  )
}

export default App

// Hello World component

import React, { useContext } from 'react'
import { UserContext } from '../UserContext'

function HelloWorld() {

    const msg = useContext(UserContext);

    return (
        <div className='container mt-3'>
            <h3>HelloWorld component : {msg}</h3>
        </div>
    )
}

export default HelloWorld

// About component

import React, { useContext } from 'react'
import { UserContext } from '../UserContext'

function About() {

    const msg = useContext(UserContext);

    return (
        <div className='container mt-3'>
            <h3>About component : {msg}</h3>
        </div>
    )
}

export default About

// Usercontext.js 

import { createContext } from "react";

export const UserContext = createContext(null);
Enter fullscreen mode Exit fullscreen mode

useMemo

备忘录或记忆化是指记住某件事的结果,而不是在需要时一遍又一遍地计算它(直到没有改变)

useMemo在 React 中,useEffect 用于那些开销较大且我们不希望它们反复运行的函数。它与useEffectHook 类似,但更多地用于函数。而 useEffect 则用于管理组件生命周期中的状态,尽管它们非常相似。

import React from 'react'

function expensivePhoneFunc (product) {
  console.log("expensivePhoneFunc")
  return product[0];
}

function App() {

  let product = [{
    name: 'Phone XL',
    price: 100
  },
  {
    name: 'Phone Mini',
    price: 80
  },
  {
    name: 'Phone Standard',
    price: 60
  }]

  const [count , setCount] = React.useState(0);

  const expensivePhone = React.useMemo( () => {
    return expensivePhoneFunc(product);
  },[])

  return (
    <div className='container mt-3'>
      <h3>Product : {expensivePhone.name}</h3>
      <h4>Price : {expensivePhone.price}</h4>
      <br />
      <h3>Count : {count}</h3>
      <button className='btn btn-primary' onClick={() => setCount(count + 1)}>+</button>
    </div>
  )
}

export default App
Enter fullscreen mode Exit fullscreen mode

useCallback

它是一种useMemo替代方法,但适用于函数,而不是函数返回的结果。它不需要反复运行函数。它通常与 useMemo 一起使用。

import React, {useCallback} from 'react'
import HelloWorld from './component/HelloWorld'

function App() {

  const [count, setCount] = React.useState(0);

  const increment = useCallback(() => {
    setCount(c => c + 1);
  }, [setCount]);


  return (
    <div>
      <HelloWorld increment={increment} />
      <h3>Count : {count}</h3>
    </div>
  )
}

export default App

// HelloWorld.jsx

import React from 'react'

const HelloWorld = React.memo(({ increment }) => {

    console.log("hello")

    return (
        <div className='container mt-3'>
            <h3>HelloWorld component</h3>
            <button onClick={increment}>Hello World</button>
        </div>
    )
})

export default HelloWorld
Enter fullscreen mode Exit fullscreen mode

文章来源:https://dev.to/dev117uday/react-hooks-0-to-hero-4b7o
PREV
CSS 中平滑逼真阴影的大师指南
NEXT
从头开始使用 React.js