美文网首页
React在项目中使用Hooks

React在项目中使用Hooks

作者: 马小帅mm | 来源:发表于2019-03-17 13:52 被阅读0次

    Hooks是React 16.8中的新增功能。它们允许您在不编写类class的情况下使用状态state和其他React功能。
    要使用Hooks请先升级你的React包,包括ReactDom

    为什么使用Hooks

    我们总是会遇到某些困惑,比如:

    • 当我们定义的一个组件只用到一两个state状态变量
    • 一个组件只用到用到了生命周期某一个方法例如componentDidMountcomponentWillUpdatecomponentWillMount

    为了解决上面的问题,我们不得不定义一个class component,来达到我们的目的,看起来代码有很多的冗余。而hooks就是帮我们解决这些问题,让我们可以抛弃class component拥抱function component,使代码看起来清晰,整洁。

    如何使用useState

    引用官方的一个例子:

    import React, { useState } from 'react';
    
    function Example() {
      // Declare a new state variable, which we'll call "count"
      const [count, setCount] = useState(0);
    
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>
            Click me
          </button>
        </div>
      );
    }
    

    看一下上面做了什么操作:

    1. 定义了一个名称为Examplefunction component
    2. 使用useState在这个方法内部定义了一个变量count
    3. 并暴露了一个修改count的方法setCount
    4. 每次点击按钮对count递增

    如果我们用class component是如何实现上面的步骤的呢?等效于:

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

    一对比我们会发现使用Hooks能让代码看起来更加有条理,简洁,也能使状态和UI层分离
    当然Hooks能让我们可以定义多个状态,每个状态都是独立的

    function ExampleWithManyStates() {
      // Declare multiple state variables!
      const [age, setAge] = useState(42);
      const [fruit, setFruit] = useState('banana');
      const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
    

    同时我们会想到如果有很多个状态,我们要写多个useStateHooks支持状态值是对象,我们也可以这样写:

      const [data, setData] = useState({age: 42, fruit: 'banana', todos: [{ text: 'Learn Hooks' }]});
    

    需要注意的是,上面的方括号[data, setData]并不是Hooks的语法,而是JavaScript数组结构

    如何使用useEffect

    我们常常需要在组件加载的时候就需要运行某些函数,异步请求数据,我们可能会这样写:

    import { Component } from 'react';
    import axios from 'axios'
    
    class Exampel extends Component {
      constructor(props) {
        super(props);
         this.state = {
           data: null
         };
      }
     
     getItem = () => {
        axios
          .post('请求url')
          .then(res => {
             this.setState{
               data: res.data
             }
          })
          .catch(err => console.error(err))
      }
    
      componentDidMount () {
        this.getItem()
      }
    
      //...
    }
    

    使用HooksuseEffect我们可以这样写:

    import { useState, useEffect } from 'react'
    import axios from 'axios'
    
    function Example(){
      const [data, setData] = useState([]);
    
      function getItem () {
        axios
          .post('请求url')
          .then(res => {
           setData(res.data)
          })
          .catch(err => console.error(err))
      }
    
      useEffect(() => {
        getItem()
      }, [])
    
      //...
    }
    

    当然我开始也是这样写的,毫无疑问这样写目前看起来没有什么问题,能达到预期的效果。
    但是有时候我会发现使用useEffect的时候,里面的函数被无限循环调用了,这当然和我想象的初始化的时候只执行一次出入很大,这是为什么呢?
    我们来看个例子:
    在发送请求的时候,往往我们会添加一些参数:

    import { useState, useEffect } from 'react'
    import axios from 'axios'
    
    function Example(){
      const [data, setData] = useState([]);
      const [params, setParams] = useState({});
    
      function getItem () {
        axios
          .post('请求url', params) //注意我在这里使用了另一个状态params
          .then(res => {
           setData(res.data)
          }) 
          .catch(err => console.error(err))
      }
    
      useEffect(() => {
        getItem()
      }, [getItem]) //在参数改变的时候调用
    
      //...
    }
    

    上面的例子,我在useEffect 里的依赖函数getItem又对返回函数data赋值,使得每次data改变的时候useEffect就会被触发,导致了无限循环。
    解决方法就是把useEffect的第二个参数改成[]也就是useEffect(() => {....}, []),这意味着效果函数应该被调用一次:仅在第一次装载/渲染之后。但是这并不符合Hooks规范。或者是你要的效果就是params改变后再次调用请求,用useCallback看看能否解决问题。

    import { useState, useEffect, useCallback } from 'react'
    import axios from 'axios'
    
    function Example(){
      const [data, setData] = useState([]);
      const [params, setParams] = useState({});
    
      const getItem = useCallback(() => { //使用useCallback解决依赖函数的不可控变量
        axios
          .post('请求url', params) //注意我在这里使用了另一个状态params
          .then(res => {
           setData(res.data)
          }) 
          .catch(err => console.error(err))
      }, [params, setData])//在useCallback里检查变量的改变
    
      useEffect(() => {
        getItem()
      }, [getItem]) //符合useEffect规范
    
      //...
    }
    

    我们推荐使用上面这种方法解决避免useEffect无限循环
    无限循环调用产生的原因主要有以下几种,你可以一个个排除看看:

    1. 是不是在依赖函数里重新设置了状态
    2. 是什么操作引起了父组件重新render,导致本身组件也被重新加载,引起无限循环
    3. useEffect是不是被放在了判断if语句或者循环for语句里面了
      当然我们还这样使用:
    useEffect(() => {
        function getItem () {
          axios
            .post('https://www.easy-mock.com/mock/5b4eb12d56c59c6f56266215/api/order_list', params) // 注意我在这里使用了另一个状态params
            .then(res => {
              setData(res.data)
            })
            .catch(err => console.error(err))
        }
        getItem()
      }, [params, setData])
    

    useEffect所有依赖的函数放在useEffect里面,让useEffect变得自给自足,这样我们减少了很多不可控制的因素,方法里面所有的依赖项都是可见的可控的。
    还有一些Hooks提供的方法useRefuseCallback等好用的方法可以查看官网API https://reactjs.org/docs/hooks-reference.html

    总结:使用Hooks的好处:

    1. 可以让我们放弃class component ,拥抱function component
    2. 可以在function component使用状态且是独立的状态,可以独立维护,这和状态组件有着很大的区别
    3. 可以让我们一个个组件去做优化,不需要全部重构
    4. 使状态与UI分离
    5. 让我们的代码看起来更加有条理、清晰、简洁

    tips: 简书上交流可能会看不到消息,如有问题,欢迎进群交流50063010
    参考链接1:https://reactjs.org/docs/hooks-intro.html
    参考链接2:https://overreacted.io/a-complete-guide-to-useeffect/

    相关文章

      网友评论

          本文标题:React在项目中使用Hooks

          本文链接:https://www.haomeiwen.com/subject/yqiymqtx.html