zoukankan      html  css  js  c++  java
  • Hook 详解(一)

    在不编写 class 的情况下使用 state(私有状态) 以及其他的 React 特性(生命周期等)。

    Hook规则

    • 在React的函数组件中调用 Hook
    • 在自定义Hook中调用其他 Hook

    Hooks的串联是一个链式的数据结构,从根节点向下通过next进行串联。因此Hooks不能嵌套使用,不能在条件判断中使用,也不能在循环中使用,否则会破坏链式结构,一旦破坏,就会读写混乱。

    Hook源码

    useState

    • 定义值
    • 定义对象
    • 定义数组
    • 定义函数
    import React, { useState } from 'react';
    
    function Index() {
      // 第一个值用于读取状态,第二个值用于修改状态
      const [count, setCount] = useState(0);
      const [obj, setObj] = useState({name: '张三'});
      const [arr, setArr] = useState([1,2,3]);
      const [func, setFunc] = useState(() => {
          return count;
      });
      
      return (
        <div className={styles.demo}>
          <div>
            <h2>You clicked {count} times</h2>
            <button onClick={() => setCount(count + 1)}>
                改变值
            </button>
          </div>
          <div>
            <h2>{obj.name}</h2>
            <button onClick={() => setObj({name: 'lisi'})}>改变对象</button>
          </div>
          <div>
            <h2>{arr}</h2>
            <button onClick={() => setArr(() => {
                arr.push(4);
                return [...arr];
            })}>改变数组</button>
          </div>
          <div>
            <h2>{func}</h2>
            <button onClick={() => setFunc(count + 1)}>
                改变函数
            </button>
          </div>
        </div>
      );
    }
    
    使用注意点:
    • 不支持局部更新
    const [myState, setMyState] = React.useState({
      name: 'guangyu',
      age: 18
    })
    
    setMyState({ age: 19 }) // 其他属性丢失
    setMyState({ ...myState, age: 19 }) // 新的 state 合并了旧属性
    
    • 当需要改变值时,必须通过setState修改,或者深拷贝一个新的数组进行更改在赋值
    const [todoLists, getLists] = useState([]);
    const list = JSON.parse(JSON.stringify(todoLists));
    list.map(item => {
              if (item.title === info.title) {
                  item.isEdit = state;
              } else {
                  item.isEdit = false;
              }
              return list;
          });
    getLists(list);
    

    useEffect

    • 相当于react中 componentDidMountcomponentDidUpdatecomponentWillUnmount 三个生命周期

    • 副作用(DOM操作, 数据请求,组件更新)

    • useEffect为什么在组件函数内部执行?可以获取props和state,采用闭包的形式

    • 无阻塞更新

    • useEffect(回调函数,数组(可不写))

    • 多个useEffect()

    Q: 为什么在组件挂载之后请求数据?

    A: 挂载前,当数据未请求回来或报错,会阻碍页面的渲染,组件挂载之后请求能实现无阻塞更新。

    当第二个参数未填时,会监听所有的状态
      useEffect(() => {
          console.log(count);
      });
    
    当第二个参数为空数组时,不监听任何状态。只在页面初始时,监听
     useEffect(() => {
          console.log(count);
      }, []);
    
    当第三个参数是某一个状态时,只监听该状态,其余不监听,实现了componentDidUpdate
    useEffect(() => {
          console.log(count);
      }, [count]);
    
    实现 componentWillUnmount
     useEffect(() => {
          console.log(count);
          // 通过函数
          return () => {
              console.log('componentWillUnmont'); // 每次都先销毁之前的
          };
      }, [count]);
    

    useRef

    useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。

    • 访问DOM,获取DOM元素
    • 保存变量
    const inputEl = useRef('123'); // 获取Dom
    const save = useRef({value: '111'}); // 保存变量
    
    <div className={styles.ref}>
        <h1>useRef</h1>
        <Input 
          type="text"
          fullWidth
          ref={inputEl}
        />
    		<br />
        <Button type="primary" onClick={() => {
            // console.log('input', inputEl.current.state.value); // 获取DOM
            save.current.value = inputEl.current.state.value  // 保存变量
            console.log('save', save);
        }}>获取ref</Button>
    </div>
    

    useContext

    使用上下文,让组件之间也能有局部的全局变量。(相当于有了this)

    • useContextcreateContext (生成一个父组件容器)一起使用

    • 接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。

    • 当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider>value prop 决定。

    • 当组件上层最近的 <MyContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值。(即使祖先使用 React.memoshouldComponentUpdate

    • useContext 的参数必须是 context 对象本身

      • 正确: useContext(MyContext)
      • 错误: useContext(MyContext.Consumer)
      • 错误: useContext(MyContext.Provider)
    // 父组件
      const MyContext = createContext();
    // 子组件
      const ChildContext = () => {
          const count = useContext(MyContext);
          return (
              <h2>我是子组件{count}</h2>   
          );
      };
      
    {/* 父子传值, value为父组件准备传给子组件的值 */}
    <MyContext.Provider value={count}>
    	  <ChildContext />
    </MyContext.Provider>
    

    useMemo

    • shouldComponentUpdate 类似作用,在渲染过程中避免重复渲染的问题(提升性能)
    • 如果没有提供依赖项数组,useMemo() 在每次渲染时都会计算新的值。
    • 当状态或父组件传来的属性发生变化时,更新组件 (也可在子组件中控制状态是否更新)
    • useMomo() 是一个函数,有两个参数,第一个参数是函数,第二个参数是数组(可不写)
    • useMomouseEffect 执行的时间不同,useEffect 是在 componentDidMount 以后执行的,而 useMemo 是在组件渲染过程中执行的

    useMomo 就是用的 memoization 来提高性能的, memoizationJavaScript 中的一种缓存技术。

    如果我们有CPU密集型操作,我们可以通过将初始操作的结果存储在缓存中来优化使用。如果操作必然会再次执行,我们将不再麻烦再次使用问的CPU,因为相同结果的结果存储在某个地方,我们只是简单的返回结果。

    因此,这个是以空间换速度。使用前需要确定是否值得这么做。

    当第二个参数不传时,会监听所有状态并更新
     const res = useMemo(() => {
          return count;
      });
    
    当第二个参数为空数组时,不管状态变不变,都不更新
     const res = useMemo(() => {
          return count;
      }, []);
    
    当第三个参数是某一个状态时,只更新该状态,其余不更新
    const [count, setCount] = useState(0);
    const [num, setNum] = useState(1);
      
    const res = useMemo(() => {
          return {
              count, num
          };
    }, [count]);
      
      <div className={styles.memo}>
          <h1>useMemo</h1>
          <h2>状态=count{res.count} --- {res.num}</h2>
          <Button onClick={() => {
          		setCount(count + 1);
          }}>change-count</Button>
          <Button onClick={() => {
          		setNum(num + 1);
          }}>change-num</Button>
      </div>
    

    虽然点击num 不更新,但在此点击count时,num的值会变成之前点击的值,这是因为num的值虽然没更新,但发生了变化,并保存在缓存中了。

    若想分别控制并更新,可以分开写,并分别传入需要控制的状态数组

    useCallback

    • 作用: 避免组件重复渲染,提高性能(和useMemo一致)
    • 可以控制组件什么时候需要更新
    • 虽同样使用到缓存技术,但和 useMemo 不同的是,useCallback 缓存的是个函数,是个函数就可以执行
    • useCallback() 有两个参数,第一个参数是函数,第二个参数是数组(可以不写)
    const [count, setCount] = useState(0);
    const [num, setNum] = useState(1);
    
    const callBack = useCallback(() => {
        console.log('count', count);
        return count;
    }, [count]);
    
    <div className={styles.callback}>
        <h1>useCallback</h1>
        <h2>callBack: {callBack()}</h2>
        <h2>count状态=count: {count}</h2>
        <h2>num状态=num: {num}</h2>
        
        <Button onClick={() => {
        		setCount(count + 1);
        }}>change-count</Button>
        
        <Button onClick={() => {
        		setNum(num + 1);
        }}>change-num</Button>
    </div>
    
  • 相关阅读:
    如何使用谷歌的网页删除请求工具?
    已有记录表添加特定排序主键ID
    用ASP实现超长内容的word模板替换objDoc.Content.Find.Execute
    内网SMTP发送失败的曲线救国之策
    IIS无法在后台生成WORD文件的故障
    WINDOWS2003进行WindowsFTP设置的一些注意事项
    解决IISASP调用XmlHTTP出现msxml3.dll (0x80070005) 拒绝访问的错误
    [转]Cate:我是如何准备Google面试的
    Ubuntu的启动配置文件grub.cfg(menu.lst)设置指南
    Linux启动过程详解
  • 原文地址:https://www.cnblogs.com/zpsakura/p/14393462.html
Copyright © 2011-2022 走看看