理解 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 并结合使用是提升代码质量的关键。

相关推荐
弄不死的强仔41 分钟前
可被electron等调用的Qt截图-录屏工具【源码开放】
前端·javascript·qt·electron·贴图·qt5
霸王蟹1 小时前
el-table组件样式如何二次修改?
前端·javascript·vue.js·笔记·学习·前端框架
star010-2 小时前
一文学会HTML编程之视频+图文详解详析
前端·网络·网络安全·html·html5
star010-2 小时前
【视频+图文详解】HTML基础3-html常用标签
前端·css·网络安全·html·html5·学习方法
m0_528723813 小时前
redex快速体验
react.js
无限大.7 小时前
前端知识速记:节流与防抖
前端
十八朵郁金香7 小时前
【VUE案例练习】前端vue2+element-ui,后端nodo+express实现‘‘文件上传/删除‘‘功能
前端·javascript·vue.js
学问小小谢8 小时前
第26节课:内容安全策略(CSP)—构建安全网页的防御盾
运维·服务器·前端·网络·学习·安全
LCG元8 小时前
Vue.js组件开发-实现全屏图片文字缩放切换特效
前端·javascript·vue.js
还是鼠鼠9 小时前
图书管理系统 Axios 源码__新增图书
前端·javascript·vscode·ajax·前端框架·node.js·bootstrap