理解 React Hooks:用例与实践

文章目录


前言

React Hooks 是 React 16.8 引入的一项强大功能,它们为函数组件引入了状态和其他 React 特性。以下是对 React 常用 Hooks 的详细介绍和使用指南。

useState:管理组件状态

状态是组件的核心部分。useState 是最基本的 Hook,用于在函数组件中添加状态。

javascript 复制代码
import React, { useState } from 'react';

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

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

要点:

  • useState 接受一个初始状态值,并返回一个状态和更新状态的函数。
  • setState 可以接受新的状态值或一个返回新状态值的函数。
useEffect:处理副作用

useEffect 是处理副作用的 Hook,例如数据获取、订阅和手动 DOM 操作。

javascript 复制代码
import React, { useState, useEffect } from 'react';

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

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]); // 仅在 count 变化时执行

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

要点:

  • useEffect 在组件渲染后执行,可以返回一个清理函数。
  • 依赖数组(第二个参数)决定 effect 的执行时机。
useLayoutEffect:同步执行副作用

useLayoutEffectuseEffect 类似,但它在所有 DOM 变更之后同步调用 effect。

javascript 复制代码
import React, { useLayoutEffect, useRef } from 'react';

function LayoutEffectExample() {
  const divRef = useRef(null);

  useLayoutEffect(() => {
    console.log(divRef.current.getBoundingClientRect());
  });

  return <div ref={divRef}>Hello World</div>;
}

要点:

  • useLayoutEffect 在 DOM 更新后立即同步执行。
  • 用于避免 DOM 更新后的闪烁,但如果计算量大可能会导致掉帧。
useReducer:复杂状态逻辑管理

useReducer 提供了一种替代 useState 的方式,用于管理更复杂的状态逻辑。

javascript 复制代码
import React, { useReducer } from 'react';

const initialState = { count: 0 };

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

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </div>
  );
}

要点:

  • useReducer 接受一个 reducer 函数和初始状态,返回当前状态和 dispatch 函数。
  • 在处理复杂状态逻辑或状态依赖的更新时非常有用。可以结合immr使用。
useRef:访问 DOM 元素和保存变量

useRef 可以保存 DOM 元素引用或其他值,改变它不会引起重新渲染。

javascript 复制代码
import React, { useRef } from 'react';

function TextInputWithFocusButton() {
  const inputEl = useRef(null);

  const onButtonClick = () => {
    inputEl.current.focus();
  };

  return (
    <div>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </div>
  );
}

要点:

  • useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(初始值)。
  • 用于直接访问 DOM 元素或保存可变值。
forwardRef 与 useImperativeHandle:自定义 ref 的内容

forwardRefuseImperativeHandle 一起使用,可以自定义父组件通过 ref 访问的子组件内容。

javascript 复制代码
import React, { useImperativeHandle, forwardRef, useRef } from 'react';

const FancyInput = forwardRef((props, ref) => {
  const inputRef = useRef();

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));

  return <input ref={inputRef} />;
});

function Parent() {
  const inputRef = useRef();

  return (
    <div>
      <FancyInput ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>Focus the input</button>
    </div>
  );
}

要点:

  • forwardRef 用于将 ref 转发到子组件。
  • useImperativeHandle 用于定义暴露给父组件的实例值。

类比:

  • forwardRef 与 useImperativeHandle 与vue3 中 porps + expose 的使用比较
useContext:跨组件传递数据

useContext 使得我们可以在组件树中传递数据,而不需要逐层传递 props。

javascript 复制代码
import React, { useContext } from 'react';

const ThemeContext = React.createContext('light');

function ThemeButton() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>Theme Button</button>;
}

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemeButton />
    </ThemeContext.Provider>
  );
}

要点:

  • useContext 接受一个 context 对象,并返回 context 的当前值。
  • 用于在组件之间共享数据而不需要逐层传递 props。
memo, useMemo 与 useCallback:性能优化

这些 hooks 用于优化组件渲染性能。

javascript 复制代码
import React, { memo, useMemo, useCallback } from 'react';

const ExpensiveComponent = memo(({ count }) => {
  return <div>{count}</div>;
});

function Parent({ count }) {
  const memoizedValue = useMemo(() => expensiveCalculation(count), [count]);

  const memoizedCallback = useCallback(() => {
    doSomething(count);
  }, [count]);

  return (
    <div>
      <ExpensiveComponent count={memoizedValue} />
      <button onClick={memoizedCallback}>Do something</button>
    </div>
  );
}

要点:

  • memo 包裹的组件仅在 props 变化时重新渲染。
  • useMemo 缓存计算结果,useCallback 缓存函数定义,避免不必要的重新创建。

结论

React Hooks 提供了强大的功能,使函数组件可以使用状态和其他 React 特性。通过理解和正确使用这些 Hooks,可以编写出更简洁、高效和可维护的 React 代码。在实际开发中,选择合适的 Hook 并结合使用是提升代码质量的关键。

相关推荐
小蜜蜂嗡嗡1 分钟前
flutter封装vlcplayer的控制器
前端·javascript·flutter
一tiao咸鱼3 分钟前
如何简单使用 prompt
前端·aigc
cdbqss18 分钟前
VB.net编写的身份证类
前端·.net
骑自行车的码农26 分钟前
React短文系列 遍历fiber树 App的创建
前端·react.js
AskSky30 分钟前
为了搞一个完美的健身APP,我真是费尽心机
前端
斯~内克35 分钟前
基于Vue.js和PDF-Lib的条形码生成与批量打印方案
前端·vue.js·pdf
阴阳怪气乌托邦36 分钟前
别再啃OA代码了!低代码"搭积木"式搞数智化,我直接少写500行
前端·低代码
beelan40 分钟前
v-on的思考
前端
山河木马43 分钟前
前端学习C++之:.h(.hpp)与.cpp文件
前端·javascript·c++
用户92724725021943 分钟前
PHP + CSS + JS + JSON 数据采集与展示系统,支持伪静态
前端