最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • React Hooks useState&useEffect&自定义hook

    正文概述 掘金(会飞的FE)   2021-05-25   644

    一、作用:

    Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
    

    二、规则

    只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用。 只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用。

    三、用法

    1、useState

    import React,{useState} from "react";
    const [state, setState] = useState(initialState);
    

    调用useState 返回一个 包含state以及更新 state 函数的数组

    在初始化时,返回的state 与传入的initialState 值相同。

    setState 函数用于更新 state,接收一个新的 state 值并将组件重新渲染。

    setState(newState); 在后续的重新渲染中,useState 返回的第一个值将始终是更新后最新的 state。

    下面介绍一下setState的两种用法,函数组件中的调用也如类组件中this.setState一样有两种写法。

    方法一:调用setState,参数为想要更新的结果,这是我们使用useState常用的更新方法。

    方法二:参数传如一个更新函数,函数的参数为上一次更新后的结果。

    下面进行两种方法的示例展示。

    const Counter = () => {
        const [count, setCount] = useState(0);
        const changeCount = () => {
          setCount(count + 1)
        }
        const addCount =() =>{
          setCount((prevCount) => {
              return prevCount + 1
          })
        }
        return (
          <div>
            {count}
            <button onClick={changeCount}>+</button>
            <button onClick={addCount}>+</button>
          </div>
        );
      }
    

    这样看起来两者似乎并没有什么不同,实现的结果也是相同的,但是我们只要稍加变化,将其包装成异步,这时候两种不同的写法却有了不同的实现结果。

    const Counter = () => {
        const [count, setCount] = useState(0);
        const changeCount = () => {
          setTimeout(() => {
            setCount(count + 1)
          }, 3000);
        }
        const addCount =() =>{
          setTimeout(() => {
            setCount((prevCount) => {
              return prevCount + 1
            })
          }, 3000);
        }
        return (
          <div>
            {count}
            <button onClick={changeCount}>+</button>
            <button onClick={addCount}>+</button>
          </div>
        );
      }
    

    如例所示,我们在定时器3s的时间内进行连续点击,第一种写法只更新了一次,结果是1,因为定义的count并没有发生变化。

    而第二种写法的结果一直保持是最新的,因为在进行更新时并不是以初始化定义的count进行加1操作,而是已上一次更新之后的结果为基础,这样保持了count的实时性。

    两种写法和类组件中的setState相似,可以直接进行更新,也可以进行函数式更新。

    const changeCount = () => {
        this.setState({count:this.state.count+1})
    }
        
    const addCount = () => {
        this.setState((prevState)=>{
           count:prevState.count+1
        })
    };
    

    另外有一点,class组件中setState更新数据是进行合并,而hooks中更新是进行数据的替换。

        constructor(props) {
            super(props);
            this.state = { count: 0, name: \'58\' };
        }
    
        this.setState((state) => {
            return {count: state.count + 1}
        })
    
        // 运行一次后的state结果是{ count: 1, name: \'58\' }
    

    2、useEffect

    副作用钩子,常在此钩子中进行数据获取,发布订阅。

    可以把useEffect看成类组件中componentDidMount,componentDidUpdate和componentWillUnmount三个生命周期的组合。

    对于useEffect的常用方法一般有两种,一种为需要清除和不需要清除的。

    不需要清除 如例所示,我们使用hooks实现一个把当前点击数量展示到document的title中的功能。

    import React, { useState, useEffect } from \'react\';
    
    function Example() {
      const [count, setCount] = useState(0);
    
      useEffect(() => {
        document.title = `You clicked ${count} times`;
      });
    
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>
            Click me
          </button>
        </div>
      );
    }
    

    这样的写法使我们的effect在每次组件重新渲染时都会进行执行一次,让我们不需要考虑当前是挂载还是更新阶段。

    在类组件中我们需要将这些副作用函数分别在componentDidMount和componentDidUpload两个生命周期函数中进行重复使用,这样才能保证实现点击次数的实时更新。

    class Example extends React.Component {
        constructor(props) {
      
         super(props);
          this.state = {
            count: 0
          };
        }
      
        componentDidMount() {
          document.title = `You clicked ${this.state.count} times`;
        }
        componentDidUpdate() {
          document.title = `You clicked ${this.state.count} times`;
        }
      
        render() {
          return (
            <div>
              <p>You clicked {this.state.count} times</p>
              <button onClick={() => this.setState({ count: this.state.count + 1 })}>
                Click me
              </button>
            </div>
          );
        }
      }
    

    相比之下useEffect看起来更加简洁,也减少了一些重复代码的书写。

    需要清除 在平时的工作中,我们可能会在代码中进行发布订阅操作,为了防止内存泄漏,常用的方法就是在componentDidMount中进行订阅,在componentWillUnmount中进行清除订阅。

      componentDidMount() {
        ChatAPI.subscribeToFriendStatus(
          this.props.friend.id,
          this.handleStatusChange
        );
      }
      componentWillUnmount() {
        ChatAPI.unsubscribeFromFriendStatus(
          this.props.friend.id,
          this.handleStatusChange
        );
      }
    

    而在hooks中我们只需要在useEffect钩子中返回一个清除函数,那么react就会在组件卸载的时候执行当前函数,进行清除操作。

        useEffect(() => {
          ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
          return () => {
            ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
          };
        })
    

    上面两个例子的effect在每次组件重新渲染时都会重新执行,这样的话就会造成一些不必要的调用或者渲染,接下来我们对上面的例子做一些优化,使其能够在effect中依赖项未发生变化时不重新执行。

    import React, { useState, useEffect } from \'react\';
    
    function Example() {
      const [count, setCount] = useState(0);
      const [number, setNumber] = useState(0);
    
      useEffect(() => {
        document.title = `You clicked ${count} times`;
      },[count]);
    
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>
            Click me
          </button>
          <button onClick={() => setNumber(number + 1)}>
            Click me
          </button>
        </div>
      );
    }
    

    在useEffect的第二个参数中,我们可以加入一个deps数组,当数组里的任意一项发生变化时,再重新执行当前的effect,这样就避免了一些不必要的调用和渲染。

    我们在调用setNumber时,count并未发生变化,所以document的title不需要进行变化,effect也就不需要重新执行。

    但当我们不把count加入依赖数组时,number的每一次变化也会使得effects重新进行一次调用,而这一步是我们所不想看到的。

    将count加入依赖数组时,每次组件重新渲染时,effect会判断当前是否发生变化,发生改变时才会重新执行当前effect,这一步我看来更像在shouldComponentUpdata中进行pervState.count===state.count判断一样。

    3、自定义hook 在使用函数组件时,难免会遇到两个组件之间存在共同逻辑。将相同逻辑拷贝在不同组件中,这未免不是一种低效的办法,这时候就轮到自定义hook发挥作用的时候了。

    通过对相同逻辑hook的封装,我们即可实现高效的办公效率和减少一些重复代码。

    自定义规则 自定义hook是一个函数,名称必须以use开头,函数内部可以调用其他的hook。

    在自定义hook中我们可以制定想要接受的参数,和在当前hook中需要做什么,以及hook返回的结果,这些规则都可以根据我们的需求进行改变。

    import { useState, useEffect } from \'react\';
    
    function useFriendStatus(friendID) {
      const [isOnline, setIsOnline] = useState(null);
    
      useEffect(() => {
        function handleStatusChange(status) {
          setIsOnline(status.isOnline);
        }
    
        ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
        return () => {
          ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
        };
      });
    
      return isOnline;
    }
    

    以上面代码为例,我们想要在不同组件中获取不同用户的登录状态,并在当前组件卸载是进行清除。对此我们把获取在线状态的hook提取出来,在组件中进行调用,只需要将当前用户ID传递就可接受一个是否在线的返回结果。

    function FriendStatus(props) {
        const isOnline = useFriendStatus(props.friend.id);
    
        return isOnline ? \'Online\' : \'Offline\';
    }
    

    四、小结

    尽管在写这篇文章之前就已经使用过很长一段时间的hooks,但在写的时候依旧有一些不知从何处写起的感觉,还是对一些内部的实现原理说的不是很清楚。通过这次对官方文档以及相关文章的阅读,也是加强了一些对hooks逻辑整理,文章如有观点偏差,还请大家指正。

    革命尚未成功,同志仍需努力.

    最后推荐一篇对于了解hook实现很有帮助的文章。overreacted.io/a-complete-…


    起源地下载网 » React Hooks useState&useEffect&自定义hook

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元