当前位置: 首页 > React学习笔记 > 正文
流量卡

深入浅出Hooks

游魂 发表于2022年6月15日 11:51

Hooks-介绍

总结

Hooks-解决的问题

  1. 组件的状态逻辑复用问题
    • 在 Hooks 之前,组件的状态逻辑复用经历了:mixins(混入)、HOCs(高阶组件)、render-props 等模式
    • (早已废弃)mixins 的问题:1 数据来源不清晰 2 命名冲突
    • HOCs、render-props 的问题:重构组件结构,导致组件形成 JSX 嵌套地狱问题
  2. class 组件自身的问题
    • 选择:函数组件和 class 组件之间的区别以及使用哪种组件更合适
    • 需要理解 class 中的 this 是如何工作的
    • 相互关联且需要对照修改的代码被拆分到不同生命周期函数中
    • 相比于函数组件来说,不利于代码压缩和优化,也不利于 TS 的类型推导

Hooks-渐进策略

  1. 什么是渐进式策略(项目开发场景)文档
    • 不推荐直接使用 Hooks 大规模重构现有组件
    • 推荐:新功能用 Hooks,复杂功能实现不了的,也可以继续用 class
    • 找一个功能简单、非核心功能的组件开始使用 hooks
  2. 在 hooks 中开发会使用那些知识?
    • class 组件相关的 API 在 hooks 中不可用
      • class 自身语法,比如,constructor、static 等
      • 钩子函数,componentDidMountcomponentDidUpdatecomponentWillUnmount
      • this 相关的用法
    • 原来学习的 React 内容还是要用的
      • JSX:{}onClick={handleClick}、条件渲染、列表渲染、样式处理等
      • 组件:函数组件、组件通讯
      • React 开发理念:单向数据流状态提升
      • 解决问题的思路、技巧、常见错误的分析等

总结

  1. react 没有计划从 React 中移除 class
  2. react 将继续为 class 组件提供支持
  3. 可以在项目中同时使用 hooks 和 class

useState-基本使用

useState 作用

useState 语法

// 参数:状态初始值(数值、字符串、数组,对象)
// 返回值:stateArray 是一个数组
const stateArray = useState(0);
// 索引 0 表示:状态值(state)
const state = stateArray[0];
// 索引 1 表示:修改状态的函数(setState)
const setState = stateArray[1];

useState 使用步骤

  1. 导入 useState hook
  2. 调用 useState 函数,并传入状态的初始值
  3. useState 函数的返回值中,拿到状态和修改状态的函数
  4. 在 JSX 中展示状态
  5. 在按钮的点击事件中调用修改状态的函数,来更新状态
import { useState } from 'react';

const Count = () => {
  // stateArray 是一个数组
  const stateArray = useState(0);
  const state = stateArray[0];
  const setState = stateArray[1];

  return (
    <div>
      {/* 展示状态值 */}
      <h1>状态为:{state}</h1>
      {/* 点击按钮,让状态值 +1 */}
      <button onClick={() => setState(state + 1)}>+1</button>
    </div>
  );
};

useState 写法正确姿势

import { useState } from 'react';

const Count = () => {
  // 解构:
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>计数器:{state}</h1>
      <button onClick={() => setState(state + 1)}>+1</button>
    </div>
  );
};

总结:

useState-读取和修改状态

读取状态

const UserCom = () => {
  const [user, setUser] = useState({ name: 'jack', age: 18 });
  return (
    <div>
      <p>姓名:{user.name}</p>
      <p>年龄:{user.age}</p>
    </div>
  );
};

修改状态

const UserCom = () => {
  const [user, setUser] = useState({ name: 'jack', age: 18 });
  const onAgeAdd = () => {
    setUser({
      ...user,
      age: user.age + 1,
    });
  };
  return (
    <div>
      <p>姓名:{user.name}</p>
      <p>年龄:{user.age}</p>
      <button onClick={onAgeAdd}>年龄+1</button>
    </div>
  );
};

总结:

useState-组件更新过程

组件初始化时候的事情

  1. 从头开始执行该组件中的代码逻辑
  2. 调用 useState(0) 将传入的参数作为状态初始值,即:0
  3. 渲染组件,此时,获取到的状态 count 值为: 0

setState 后发生的事情

  1. 点击按钮,调用 setCount(count + 1) 修改状态,因为状态发生改变,所以,该组件会重新渲染
  2. 组件重新渲染时,会再次执行该组件中的代码逻辑
  3. 再次调用 useState(0),此时 React 内部会拿到最新的状态值而非初始值,比如,该案例中最新的状态值为 1
  4. 再次渲染组件,此时,获取到的状态 count 值为:1
import { useState } from 'react';
const Counter = () => {
  const [count, setCount] = useState(0);
  return (
    <div>
      <h1>计数器:{count}</h1>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
};

总结:

useState-使用原则

定义多个状态的原则

hook 函数书写位置原则

可以通过开发者工具进行查看组件的 hooks

01.053a5fe6

总结:

useEffect-副作用

  1. side effect 副作用专业解释
    • 在计算机科学中,如果一个函数或其他操作修改了其局部环境之外的状态变量值,那么它就被称为有副作用
    • 在函数组件中:职责就是根据状态渲染 UI,其他的事情都是副作用
  2. 通过生活例子,理解副作用
    • 999 感冒灵,主作用:用于感冒引起的头痛,发热,鼻塞,流涕,咽痛等
    • 副作用:可见困倦、嗜睡、口渴、虚弱感
  3. 使用函数组件常见的副作用
    • 对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用(比如,手动修改 DOM)
    • 常见的副作用(side effect):数据(Ajax)请求、手动修改 DOM、localStorage、console.log 操作等
    • 当你想要在函数组件中,处理副作用(side effect)时,就要使用 useEffect Hook 了

总结:

useEffect-基本使用

语法介绍

useEffect(() => {
  // 书写副作用代码
});

例:count 更新的时候显示到标题

import { useEffect } from 'react';

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

  useEffect(() => {
    document.title = `当前已点击 ${count} 次`;
  });

  return (
    <div>
      <h1>计数器:{count}</h1>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
};

总结:

useEffect-依赖项

默认使用 useEffect 的问题

useEffect 依赖项的使用

import { useEffect } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    document.title = `当前已点击 ${count} 次`;
  }, [count]);

  return (
    <div>
      <h1>计数器:{count}</h1>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={() => setCount(!loading)}>切换 loading</button>
    </div>
  );
};

总结:

useEffect-不要对依赖项撒谎

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

  // 错误演示:
  useEffect(() => {
    document.title = '点击了' + count + '次';
  }, []);

  return (
    <div>
      <h1>计数器:{count}</h1>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
};

总结:

useEffect-依赖是一个空数组

  1. useEffect 的第二个参数,还可以是一个空数组([])
    1. 表示只在组件第一次渲染后执行 effect
    2. 该 effect 只会在组件第一次渲染后执行,因此可以执行像事件绑定等只需要执行一次的操作。
    3. 相当于 class 组件的 componentDidMount 钩子函数的作用
useEffect(() => {
  const handleResize = () => {};
  window.addEventListener('resize', handleResize);
}, []);

注意:

useEffect-清除副作用

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


  // 这个返回的函数,会在该组件卸载时来执行
  // 因此,可以去执行一些清理操作,比如,解绑 window 的事件、清理定时器 等
  return () => window.removeEventListener('resize', handleResize);
}, []);

useEffect-使用总结

// 1
// 触发时机:1 第一次渲染会执行 2 每次组件重新渲染都会再次执行
// componentDidMount + ComponentDidUpdate
useEffect(() => {});

// 2(使用频率最高)
// 触发时机:只在组件第一次渲染时执行
// componentDidMount
useEffect(() => {}, []);

// 3(使用频率最高)
// 触发时机:1 第一次渲染会执行 2 当 count 变化时会再次执行
// componentDidMount + componentDidUpdate(判断 count 有没有改变)
useEffect(() => {}, [count]);

// 4
useEffect(() => {
  // 返回值函数的执行时机:组件卸载时
  // 在返回的函数中,清理工作
  return () => {
    // 相当于 componentWillUnmount
  };
}, []);

useEffect(() => {
  // 返回值函数的执行时机:1 组件卸载时 2 count 变化时
  // 在返回的函数中,清理工作
  return () => {};
}, [count]);

Hooks进阶

自定义hook

例:

未封装前

App.js

import { useState, useEffect } from "react"

const App = () => {

  const [mouse, setMouse] = useState({ x: 0, y: 0 })
  useEffect(() => {
    const handelMouseMove = (e) => {
      setMouse({ x: e.pageX, y: e.pageY })
    }
    document.addEventListener('mousemove', handelMouseMove)
    return () => {
      document.removeEventListener('mousemove', handelMouseMove)
    }
  }, [])

  return <div className="app">根组件 {JSON.stringify(mouse)}</div>
}

export default App

封装hooks之后

hook.js

import { useState, useEffect } from "react"

export const useMouse = () => {
  const [mouse, setMouse] = useState({ x: 0, y: 0 })
  useEffect(() => {
    const handelMouseMove = (e) => {
      setMouse({ x: e.pageX, y: e.pageY })
    }
    document.addEventListener('mousemove', handelMouseMove)
    return () => {
      document.removeEventListener('mousemove', handelMouseMove)
    }
  }, [])
  return mouse
}

App.js

import { useMouse } from "./hooks" 

const App = () => {

  const mouse = useMouse()

  return <div className="app">根组件 {JSON.stringify(mouse)}</div>
}

export default App

useRef-基本使用

使用useRef可以获取dom元素,组件也可以通过它获取。

例:组件初始化自动获取焦点功能

import { useRef, useEffect } from "react";

const App = () => {
  const inputRef = useRef(null);

  useEffect(() => {
    // input DOM元素
    console.log(inputRef.current); 
    // input 获取焦点
    inputRef.current.focus()
  }, []);

  return (
    <div className="app">
      根组件:
      <input type="text" ref={inputRef} />
    </div>
  );
};

export default App;

useContext-基本使用

大致步骤:

  1. 通过 createContext 创建context对象

    context.js

    import { createContext } from "react";
    export default createContext({})
    
  2. 通过 Provider 组件包裹根组件,注入数据

    App.jsx

    import { useState } from "react";
    import Context from "./context";
    import Child from './Child'
    
    const App = () => {
      const [count, setCount] = useState(0)
      return (
        <Context.Provider value={count}>
          <div className="app">
            根组件:{count} <button onClick={() => setCount(count + 1)}>打豆豆</button>
            <hr />
            <Child />
          </div>
        </Context.Provider>
      );
    };
    export default App;
    
  3. 后代 组件中使用 useContext 使用数据

    Child.jsx

    import { useContext } from "react"
    import Context from './context'
    const Child = () => {
      const count = useContext(Context)
      return <div>后代组件:{count}</div>
    }
    
    export default Child
    

总结:

全文完
本文标签: hooksreacthooksuseStateuseEffectuseRefuseContext
本文标题: 深入浅出Hooks
本文链接: https://www.iyouhun.com/m/?post=226

〓 随机文章推荐

共有2012阅 / 0我要评论
  1. 还没有评论呢,快抢沙发~

发表你的评论吧返回顶部

!评论内容需包含中文

请勾选本项再提交评论