【react常见hooks】

除了 React 官方内置的常用 Hooks(像 useState、useEffect、useContext、useReducer、useCallback、useMemo、useRef 等 ),开发者还能自己写 自定义 Hooks(把重复逻辑封装成 Hook ,方便复用 )。

useState

import React, { useState } from 'react';
function Counter() {
  // count 是当前状态,setCount 是更新它的函数,初始值设为 0
  const [count, setCount] = useState(0); 
  return (
    <div>
      <p>当前计数: {count}</p>
      {/* 点击按钮,调用 setCount 让 count +1 ,触发组件重新渲染 */}
      <button onClick={() => setCount(count + 1)}>增加计数</button> 
    </div>
  );
}

useContext

import { createContext, useContext } from "react";

export default function App5() {
  return (
    <div>
      <Section>
        <Heading>标题1</Heading>
        <Section>
          <Heading>标题2</Heading>
          <Section>
            <Heading>标题3</Heading>
            <Section>
              <Heading>标题4</Heading>
              <Section>
                <Heading>标题5</Heading>
                <Section>
                  <Heading>标题6</Heading>
                </Section>
              </Section>
            </Section>
          </Section>
        </Section>
      </Section>
    </div>
  );
}

const levelContext = createContext(0);

const Heading = ({ children }) => {
  const level = useContext(levelContext);
  switch (level) {
    case 1:
      return <h1>{children}</h1>;
    case 2:
      return <h2>{children}</h2>;
    case 3:
      return <h3>{children}</h3>;
    case 4:
      return <h4>{children}</h4>;
    case 5:
      return <h5>{children}</h5>;
    case 6:
      return <h6>{children}</h6>;
    default:
      throw new Error(`Unsupported level: ` + level);
  }
};

// 每次调用会走一遍而不是最终值,这是关键,遇到一次执行一次
const Section = ({ children }) => {
  const level = useContext(levelContext);
  return (
    <section className="section">
      <levelContext.Provider value={level + 1}>
        {children}
      </levelContext.Provider>
    </section>
  );
};

useEffect

import { useEffect, useState } from "react";
// useEffect用法
/**
 * 说明
useEffect 的本质:在组件渲染后执行副作用代码,并在依赖变化或组件卸载时清理副作用。
依赖数组 [count]:只有 count 变化时才会重新执行 effect。
返回的函数就是“清理函数”,在 effect 重新执行前或组件卸载时调用。
本质:与组件渲染相关,与组件生命周期相关,额外执行的代码
 */

export default function App7() {
  const [count, setCount] = useState(0);

  // useEffect 的本质:每次渲染后执行副作用
  useEffect(() => {
    console.log("组件渲染或 count 变化时执行,当前count:", count);

    // 返回一个清理函数,组件卸载或依赖变化前执行
    return () => {
      console.log("清理副作用,count:", count);
    };
  }, [count]); // 依赖 count,count 变化时重新执行

  return (
    <div>
      <p>count: {count}</p>
      <button onClick={() => setCount(count + 1)}>增加</button>
    </div>
  );
}

useEffect(() => {
  window.addEventListener('resize', handleResize);

  return () => {
    window.removeEventListener('resize', handleResize); // 清理
  };
}, []);
/**
useEffect(() => { ... }, []) 的含义
[] 表示依赖项为空,即这个 useEffect 只会在组件首次挂载(渲染)时执行一次,之后组件更新时不会再执行。
只有在组件卸载时,才会执行 return 的清理函数。
*/

如果没有 return 这个清理函数,事件监听会一直存在,组件卸载后还会响应事件,造成内存泄漏和异常。
总结:
return 的清理函数是用来“撤销”上一次副作用,防止副作用残留,保证组件行为正确、资源不泄漏。
如果你有具体的副作用代码,我可以帮你分析是否需要清理函数!

类似于java spring里面的后置处理器,可以夹带私货

useReducer

// useReducer
import { useReducer } from "react";
const countReducer = (state, action) => {
  switch (action.type) {
    case "increment":
      return state + 1;
    case "decrement":
      return state - 1;
    default:
      throw new Error("Invalid action");
  }
};
// useReducer 看成是协议/绑定的关键词, [count, dispatch] 和 countReducer 绑定,分别又对应state, action 
export default function App6() {
  const [count, dispatch] = useReducer(countReducer, 0);
  return (
    <div>
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
      <span
        style={{
          margin: "0 10px",
          // 固定宽度
          width: "100px",
          textAlign: "center",
        }}
      >
        {count}
      </span>
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
    </div>
  );
}

useCallback

// useCallback
/**
 * 场景说明
假设你有一个父组件,里面有一个回调函数传递给子组件。
每次父组件渲染时,如果不用 useCallback,
回调函数都会重新创建,导致子组件不必要的重新渲染。
 * 
 */
import React, { useState, useCallback } from "react";

// 子组件,只有 props.onClick 变化才会重新渲染
const Child = React.memo(({ onClick }) => {
  console.log("Child 渲染");
  return <button onClick={onClick}>点我</button>;
});

function Parent() {
  const [count, setCount] = useState(0);
  console.log("Parent 渲染");

  // 没有 useCallback,每次 Parent 渲染都会生成新函数
  // const handleClick = () => {
  //   console.log('点击了');
  // };

  // 用 useCallback,只有 count 变化时才会生成新函数
  const handleClick = useCallback(() => {
    console.log("点击了");
  }, []); // 依赖项为空,handleClick 永远不会变

  return (
    <div>
      <p>count: {count}</p>
      <button onClick={() => setCount(count + 1)}>加一</button>
      <Child onClick={handleClick} />
    </div>
  );
}

export default Parent;

/**
 * React.memo 是 React 提供的一个高阶组件(Higher Order Component,简称 HOC),
 * 用于优化函数组件的性能。
 * 作用
它的作用是:记忆(缓存)组件的渲染结果,只有当 props 发生变化时,才会重新渲染组件。
如果 props 没变,React 会跳过渲染,直接复用上一次的结果。
 */

点击 加一,父组件本质会执行函数(组件就是函数),此时 Child 理论上函数也会执行;但是由于用了 React.memo,Child 只要 Props属性不变,Child是可以只加载一次,但上面的函数不用useCallback,发现Child 会跟随加载,==》 表明 Props 发生改变,就表明父组件的 handleClick 有更新加载到内存(并未触发);
如果父组件传给子组件的函数使用了useCallback,那么子组件只会走一次,后续只有父组件在走,

// 看一个例子
const handleClick = (() => {
  console.log("点击了");
})(); 
# 如果使用立即执行函数

注意:
这里的 handleClick 实际上不是一个函数,而是立即执行函数表达式(IIFE)的返回值。
也就是说,这段代码会在组件渲染时立刻执行一次 console.log("点击了"),并且 handleClick 的值是 undefined(因为 IIFE 没有返回值)。
React.memo 会对 props 做浅比较,发现 onClick 没变(一直是 undefined),所以 Child 不会重新渲染

useMemo

import { useState, useMemo } from "react";

// useMemo
export default function App13() {
  const [x, setX] = useState(0);
  const [y, setY] = useState(0);

  console.time("xxx");
  const a = useMemo(() => {
    let a = 0;
    for (let i = 0; i < 1000000000; i++) {
      a += y;
    }
    return a;
  }, [y]);
  console.timeEnd("xxx");

  return (
    <div>
      <div>x : {x}</div>
      <div>y : {y}</div>
      <div>a : {a}</div>
      <button onClick={() => setX(x + 1)}>x + 1</button>
      <button onClick={() => setY(y + 1)}>y + 1</button>
    </div>
  );
}

useRef

export default function App14() {
  let timer;
  const handleClick = () => {
    clearInterval(timer);
    timer = setInterval(() => {}, 1000);
  };
  // useRef
  return (
    <>
      <button onClick={handleClick}>开始</button>
      <div>hello world</div>
    </>
  );
}

/**
 * 存在一个问题,组件每次渲染,重新执行handleClick,导致定时器重新开始
 * 
 * export default function App14() {
  let timer = useRef(null);
  const handleClick = () => {
    clearInterval(timer.current);
    timer.current = setInterval(() => {}, 1000);
  };
  // useRef
  return (
    <>
      <button onClick={handleClick}>开始</button>
      <div>hello world</div>
    </>
  );
}
 */

1
2

posted @ 2025-06-17 16:25  十三山入秋  阅读(23)  评论(3)    收藏  举报