📅 GJ504b 的 React 进阶之路:Day 2

📅 GJ504b 的 React 进阶之路:Day 2

一、useEffect 基础认知:解决“副作用”问题

1. 核心作用

useEffect 专门处理 React 组件中非渲染逻辑的“副作用”,在代码中实战了 3 类核心副作用,对应代码如下:

// 1. DOM 操作(Counter 组件)—— 打印 DOM 元素内容
export default function Counter() {
  const [count, setCount] = useState(0);
  // 👇 副作用:操作 DOM 元素
  useEffect(() =>{
      console.log(document.querySelector('.count')?.innerText)
  },[count])
  // ...其余代码
}

// 2. 网络请求(ColorGet 组件)—— fetch 接口获取颜色
export default function ColorGet(){
  const [color,setColor] = useState('')
  const [flag,setFlag] = useState(false)
  // 👇 副作用:网络请求
  useEffect(()=>{
      const controller = new AbortController()
      if (!flag) return
      fetch('https://api.liulongbin.top/v1/color',{signal:controller.signal})
      // ...请求处理逻辑
  },[flag])
  // ...其余代码
}

// 3. 定时器(PatientTable 组件)—— setTimeout 模拟异步请求
export default function PatientTable(){
    const[list,setList] = useState([]);
    // 👇 副作用:定时器
    useEffect(() =>{
        const timer = setTimeout(() =>{
            const mockPatient = [/* 模拟数据 */];
            setList(mockPatient)
        },1000);
        // 清理函数
        return () => clearTimeout(timer);
    },[]);
    // ...其余代码
}

2. 基础语法


useEffect(() => {
  // 【副作用函数】:执行网络请求(核心逻辑)
  const controller = new AbortController()
  if (!flag) return
  fetch('https://api.liulongbin.top/v1/color',{signal:controller.signal})
  .then((res) =>res.json())
  .then((res) =>{
      console.log(res)
      setColor(res.data.color)
  })
  .catch((err)=>console.log("消息:" + err.message))
  
  // 【清理函数】:中止未完成的请求,避免内存泄漏
  return () => controller.abort()
}, [flag]); // 依赖项数组

二、核心规则:依赖项数组的“优先级”

useEffect(() =>{
    console.log(document.querySelector('.count')?.innerText)
},[])//1. 空数组的位置是依赖项,只会执行1次

useEffect(() =>{
    console.log(document.querySelector('.count')?.innerText)
})//2. 但是如果不写依赖项就代表每次组件渲染后再执行

useEffect(() =>{
    console.log(document.querySelector('.count')?.innerText)
},[count])//3. 只在组件渲染和判断count是否改变后再执行

避坑点

export default function Counter() {
  const [count, setCount] = useState(0);
  // ❌ 禁止:useEffect 内修改自身依赖项(会无限循环)
  // 注释4:以下代码会导致无限循环
  // useEffect(() =>{
  //     setCount((prev) =>prev + 1)
  //     console.log(document.querySelector('.count')?.innerText)
  // },[count])

  // ✅ 规范:多个副作用分开声明(注释5)
  // 比如“DOM 监听”和“网络请求”分开写
  useEffect(() => { /* DOM 操作 */ }, [count]);
  useEffect(() => { /* 若有请求 */ }, []);
}

补充:React 严格模式的影响

export default function Counter() {
  const [count, setCount] = useState(0);
  // 👆 组件顶层代码:开发环境严格模式下会执行 2 次(比如直接写 console.log 会打印两次)
  
  useEffect(() =>{
      console.log(document.querySelector('.count')?.innerText)
  },[count])
  // 👆 useEffect 内代码:React 特殊处理,仅执行 1 次(初始渲染时)
}

三、进阶用法:清理函数(return 函数)

// ColorGet 组件中清理函数的触发时机注释
useEffect(()=>{
    const controller = new AbortController()
    if (!flag) return
    fetch('https://api.liulongbin.top/v1/color',{signal:controller.signal})
    // ...请求逻辑
    
    /* 清理函数的触发时机:
    1. 组件被卸载时
    2. effect副作用函数被执行之前,先执行清理函数 */
    return () => controller.abort()
},[flag])

四、实战落地:useEffect 结合业务逻辑

1. ColorGet 组件:条件触发网络请求

export default function ColorGet(){
  const [color,setColor] = useState('')
  const [flag,setFlag] = useState(false)
  // 👇 核心:仅 flag 为 true 时发起请求
  useEffect(()=>{
      const controller = new AbortController()
      if (!flag) return // 条件判断:flag 为 false 直接终止
      fetch('https://api.liulongbin.top/v1/color',{signal:controller.signal})
      .then((res) =>res.json())
      .then((res) =>{
          console.log(res)
          setColor(res.data.color)
      })
      .catch((err)=>console.log("消息:" + err.message))
      return () => controller.abort()
  },[flag]) // 依赖 flag,点击按钮(flag 变化)时执行
  
  return(
      <>
          <button onClick={()=>setFlag((prev) => !prev)}>Click it</button>
          {flag && <p>color 的颜色是:{color}</p>}
      </>
  )
}

alt text

alt text

2. PatientTable 组件:挂载时模拟异步请求

export default function PatientTable(){
    const[list,setList] = useState([]);
    // 👇 依赖空数组:仅挂载时执行 1 次
    useEffect(() =>{
        const timer = setTimeout(() =>{
            const mockPatient = [
                {name:'张三',state:'已上链',age:18},
                {name:'李四',state:'未上链',age:33},
                {name:'王五',state:'已上链',age:16}
            ];
            setList(mockPatient)
            console.log('数据到了!')
        },1000);
        // 清理函数:避免组件卸载后定时器执行
        return () => clearTimeout(timer);
    },[]);
    
    // 交互优化:加载中/数据展示
    return (
        <div>
            <h3>最近访问者</h3>
            {
                list.length ===0 ?<p>正在调取数据</p> : (
                    <ul>{/* 渲染列表 */}</ul>
                )
            }
        </div>
    )
}

alt text

alt text

3. Counter 组件:监听状态变化操作 DOM

export default function Counter() {
  const [count, setCount] = useState(0);
  function add() {
    setCount((count) =>count + 1);
  }
  // ❌ 直接顶层操作 DOM:无法实时更新
  // console.log(document.querySelector('.count')?.innerText)
  
  // ✅ useEffect 操作 DOM:确保拿到最新 DOM
  useEffect(() =>{
      console.log(document.querySelector('.count')?.innerText)
  },[count])

  return (
    <div>
      <div className="count">count 的值:{count}</div>
      <button onClick={add}> + 1 </button>
    </div>
  );
}

alt text

核心要点回顾

  1. useEffect 核心是处理副作用(DOM/请求/定时器),依赖项数组决定执行时机:无依赖=每次渲染、空数组=仅挂载、特定状态=状态变化时执行;
  2. 清理函数是“内存保护手段”:定时器要 clearTimeout、网络请求要 abort,避免内存泄漏;
  3. 实战技巧:通过 if (!flag) return 条件判断控制副作用执行时机,贴合业务逻辑;
  4. 严格模式特性:组件顶层代码执行 2 次,useEffect 内代码仅执行 1 次(开发环境)。

力扣

题目描述

alt text

解题(想不出来,最后看的题解)

还是js内置方法不够灵活应用

/**
 * @param {string[]} strs
 * @return {string[][]}
 */
var groupAnagrams = function(strs) {
    const n = strs.length;
    const map = new Map();
    for(let i = 0;i<n;i++){
        let arr = Array.from(strs[i]);//把字符串变成字符数组,便于后续排序等操作
        arr.sort();
        let key = arr.toString()//将排好序的字符数组转成字符串作为哈希表的键
        let list = map.get(key) ?map.get(key):new Array();//将键存入分组数组list(有这个键就直接存放,没有就开辟新数组空间存放),相当与“指针”
        list.push(strs[i]);//把与指针对应的值(原始值)存入对应数组
        map.set(key,list);//把更新后的list数组和键放在map哈希表中
    }
    return Array.from(map.values());//返回想要的值部分
};

posted @ 2026-02-02 23:02  GJ504b  阅读(18)  评论(0)    收藏  举报