浅谈 react Hooks

  1. 首先 - hook 是React.js在16.8之后推出的一个新特性,它可以让你在不编写class组件下使用state及react其他的特性。
  • useState - 第一个简单的 Hook,话不多说直接上代码。
    import React, { useState } from 'react';
    
    function Example() {
      // 声明一个新的叫做 “count” 的 state 变量,如果想改变这个count值,只需要调用setCount即可
      const [count, setCount] = useState(0);
    
      return (
        <div>
          <p>You 点击 {count} 加一 </p>
          <button onClick={() => setCount(count + 1)}>
            Click me
          </button>
        </div>
      );
    }

    原理:单个state值时。

    let memoizedState; // 首先是在外部定义了一个 state变量值。
    
    function useState (initialState) { 
      memoizedState = memoizedState || initialState    
      function setState (newState) {    
        memoizedState = newState    
        render()    
      }    
      return [memoizedState, setState]    
    }

    多个state时,这时候就需要优化我们的 Hooks ,解决多个 useState 同时使用的问题,当多个状态存在的时候,我们需要使用数组保存状态。

    let memoizedStates = []    
    let index = 0    
    function useState (initialState) {    
      memoizedStates[index] = memoizedStates[index] || initialState    
      let currentIndex = index    
      function setState (newState) {    
        memoizedStates[currentIndex] = newState    
        render()    
      }    
      return [memoizedStates[index++], setState]    
    }    
        

    End: 就是这么简单。

  •  useEffect: 它是可以让你在函数组件中执行副作用操作,如果你熟悉 React class 的生命周期函数,你可以把 useEffect 当做 componentDidMountcomponentDidUpdate 和 componentWillUnmount 这三个函数的组合
  • 常见的副作用( 无需清除,需要清除
  • 无需清除的 Effect 有时候我们需要在 React Dom更新之前做一些操作时 ,比如发起网络请求,手动更新dom、记录日志,这些都是常见的无需清除的操作,这些执行之后就可以忽略他们了,class方式,就不必再说,直接上代码
    import React, { useState, useEffect } from 'react';
    
    function Example() {
      const [count, setCount] = useState(0);
    
      useEffect(() => {
        document.title = `点击 ${count} 加一`;
      });
    
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>
            点击
          </button>
        </div>
      );
    }

    这是简单的一个 Effect 使用方法,当然还遇到过这种的。

    import React, { useState, useEffect } from 'react';
    
    function Example() {
      const [count, setCount] = useState({});
    
      useEffect(() => {
       setCount({test:"count是一个对象,使得页面死循环"})
      },[count]);
    
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>
            点击
          </button>
        </div>
      );
    }

    执行的结果:死循环

  • 原因:

    useEffect接受两个参数:

  • 第一个参数是函数(这里叫effect函数),它的作用是,在页面渲染后执行这个函数。因此你可以把ajax请求等放在这里执行;
  • 第二个参数是一个数组,这里注意:
    参数现象注意点
    不传每次渲染后都执行清理或者执行effect这可能会导致性能问题,比如两次渲染的数据完全一样
    空数组只运行一次的 effect(仅在组件挂载和卸载时执行)这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行,适用于页面初次加载时进行近执行一次的操作
    传 [ count ]React 将对前一次渲染的count和后一次渲染的count进行比较。若相等React 会跳过这个 effect,实现了性能的优化

    所以就上面例子中之所以造成页面的死循环的情况,是因为在JavaScript中,{} === {}结果是false,{a:1} === {a:1}同样,因此造成了react以为两个值不同,就一直的渲染最终页面死循环。

  • 需要清除的副作用  

  • 当我们组件内,订阅外部文件,定时器 ,全局的某些变量时,组件卸载时,我们需要把这些清理掉。

  • 我们拿一个定时器距离来说

    function Counter() {
        const [count, setCount] = useState(0);
        useEffect(() => {
            const id = setInterval(() => {
                setCount(count + 1);
                // 不想用到外部状态可以用 setCount(count => count + 1);
            }, 1000);
        }, [count]); // 确保所有状态依赖都放在这里
        console.log(count);
        return <h1>{count}</h1>;
    }

    这是一个简单的 Effect写法,它的第二个参数,就上面来说是依赖于count变化而执行第一个参数,这样页面就是不断的打印新的count,问题来了,每次更新时,会重新运行 useEffect 的回调函数,也就会重新设置一个定时器。问题又来了,我们上一次设置的定时器并没有清理掉,所以频繁的更新会导致越来越多的定时器同时在运行。 为了解决上面的问题,就需要用到 useEffect 的另一个特性:清除副作用。

    function Counter() {
        const [count, setCount] = useState(0);
        useEffect(() => {
            const id = setInterval(() => {
                setCount(count + 1);
            }, 1000);
            // 返回一个清理副作用的函数
            return () => {
                clearInterval(id);
            }
        }, [count]);
        console.log(count);
        return <h1>{count}</h1>;
    }

    这样就好啦 。

  • useRef,对于ref 来说,就是在react世界里虽然推崇的虚拟dom,合成事件 ,但是无法避免我们有时候需要找到指定的dom节点时,例如,在react里如果做一张图表时,一般我们是引入图表的UI类库,echarts,antv ,或者canvas时,就需要我们能找到装载图表的div,在16.8之前是又  字符串、回调函数,两种来获取dom节点而Hook, 也是有新的一种方式。
  • Ref - 是什么?
    const refDom= useRef(initialValue);
  • 会返回一个可变的 ref 对象,该对象只有个 current 属性,初始值为传入的参数( initialValue )。
  • 返回的 ref 对象在组件的整个生命周期内保持不变
  • 当更新 current 值时并不会 re-render ,这是与 useState 不同的地方
  • 更新 useRef 是 side effect (副作用),所以一般写在 useEffect 或 event handler 里
  • useRef 类似于类组件的 this

简单示例

import React, { MutableRefObject, useRef } from 'react';
const TextInputWithFocusButton: React.FC = () => {
   const inputEl: MutableRefObject<any> = useRef(null)
   const handleFocus = () => {
       // `current` 指向已挂载到 DOM 上的文本输入元素
       inputEl.current.focus()
   }
   return (
       <p>
           <input ref={inputEl} type="text" />
           <button onClick={handleFocus}>Focus the input</button>
       </p>
   )
}
export default TextInputWithFocusButton

 通过useRef定义个inputEl变量,在input 元素上定义ref={inputEl},这样通过inputEl.current就可以获取到input Dom 元素,选中则调用下focus函数即可.

更多关于Ref 问题 转载  https://blog.csdn.net/u011705725/article/details/115634265 

自定义 Hook  

节流函数和防抖函数想必大家都会熟悉,为了让我们在开发中更优雅的使用节流和防抖函数,我们往往需要让某个state也具有节流防抖的功能,或者某个函数的调用,为了避免频繁调用,我们往往也会采取节截流防抖,原生的节流防抖函数可能如一下代码所示:

// 节流
function throttle(func, ms) {
    let previous = 0;
    return function() {
        let now = Date.now();
        let context = this;
        let args = arguments;
        if (now - previous > ms) {
            func.apply(context, args);
            previous = now;
        }
    }
}
 
// 防抖
function debounce(func, ms) {
    let timeout;
    return function () {
        let context = this;
        let args = arguments;
 
        if (timeout) clearTimeout(timeout);
        
        timeout = setTimeout(() => {
            func.apply(context, args)
        }, ms);
    }
}

主角来了一下防抖hooks,代码如下:

import { useEffect, useRef } from 'react'
 
const useDebounce = (fn, ms = 30, deps = []) => {
    let timeout = useRef()
    useEffect(() => {
        if (timeout.current) {
          clearTimeout(timeout.current)
         }
        timeout.current = setTimeout(() => {
            fn()
        }, ms)
    }, deps)
    const cancel = () => {
        clearTimeout(timeout.current)
        timeout = null
    }
  
    return [cancel]
  }
 
export default useDebounce

useDebounce接受三个参数,分别是回调函数,时间间隔以及依赖项数组,它暴露了cancel API,主要是用来控制何时停止防抖函数用的。具体使用如下:


import { useDebounce } from 'hooks'
const Home = (props) => {
  const [a, setA] = useState(0)
  const [b, setB] = useState(0)
  const [cancel] = useDebounce(() => {
    setB(a)
  }, 2000, [a])
 
  const changeIpt = (e) => {
    setA(e.target.value)
  }
  return <div>
    <input type="text" onChange={changeIpt} />
    { b } { a }
  </div>

同理,我们继续来实现节流的hooks函数。

import { useEffect, useRef, useState } from 'react'
 
const useThrottle = (fn, ms = 30, deps = []) => {
    let previous = useRef(0)
    let [time, setTime] = useState(ms)
    useEffect(() => {
        let now = Date.now();
        if (now - previous.current > time) {
            fn();
            previous.current = now;
        }
    }, deps)
 
    const cancel = () => {
        setTime(0)
    }
  
    return [cancel]
  }
 
export default useThrottle

代码和自定义useDebounce类似,但需要注意一点就是为了实现cancel功能,我们使用了内部state来处理,通过控制时间间隔来取消节流效果,当然还有很多其他方法可以实现这个 API

End: 当然最开始学习Hook,还是去官网 链接 Hook 简介 – React 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小玩家儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值