react hook 初体验

定义:Hook 是一些可让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。html

做用: React Hooks 要解决的是状态共享问题,可是只共享数据的处理逻辑,不会共享数据自己。react

1、优点

  1. 组件间复用状态逻辑复杂
  2. 难于理解的class

2、State Hook基础

useState 就是一个hook,它能够给组件添加内部state,它会返回一对值,当前状态和跟新它的函数。这个函数相似于class组件的this.setState 。 下面这个例子是最简单的计数器:ios

import React, { useState } from 'react'
function Example({
  const [count, setCount] = useState(0)
  return (
    <div>       <button onClick={add}>点击+1</button>       <p>点击次数:{count}</p>     </div>
  )
  function add ({
    setCount (count + 1)
  }
}
export default Example
复制代码

Image.png

3、Effect Hook

useEffect 默认状况下在每次更新以后都会执行,使用useEffect能够方便的访问state或其余props。 一个完整的effect的格式以下:json

useEffect(() => {
        effect
        return () => {
            cleanup
        };
    }, [input])
复制代码

其中是effect是你须要执行的方法,return不是必须的,它返回的是一个清除机制。第二个参数[input]也不是必须的,指的是effect的依赖。若是第二个参数是[]表示的是仅在挂载和卸载的时候执行,不传表示在挂载和更新的时候执行。axios

4、Capture Value 特性

解释:每次 Render 的内容都会造成一个快照并保留下来,所以当状态变动而 Rerender 时,就造成了 N 个 Render 状态,而每一个 Render 状态都拥有本身固定不变的 Props 与 State。什么是Cature Value 以下面一个例子: `数组

function Example({
  const [count, setCount] = useState(0)
  return (<div><button onClick={add}>点击测试</button></div>)
  function add({
    setCount(5)
    setTimeout(() => {
      // 结果是0而不是5
      console.log(count)
    }, 1000)
  }
}
复制代码

打印的结果是0,而不是5,开始很让人费解。根据Capture Value特性,第一次挂载组件的时候,count就已经造成了一份快照,并保留下来了。setTimeout里面操做的count就是最开始的快照。 另外一个例子说明了这个问题:bash

function Example({
    const [count, setCount] = useState(0);
    useEffect(() => {
        setTimeout(() => {
            console.log(`You clicked ${count} times`);
        }, 3000);
    });
    return (
        <div>             <p>You clicked {count} times</p>             <button onClick={() => setCount(count + 1)}>                 Click me         </button>         </div>
    );
}
复制代码

Image [1].png
在快速点击按钮多下以后,上面结果说明每次更新都有一个快照保存了下来,每次打印出来的是对应的快照内容,而不是最后一次。接着你会想,我如何才能取到当前真正的值呢,而不是旧值。你须要使用 useRef

function Example({
    const [count, setCount] = useState(0);
    const latestCount = useRef(count);
    useEffect(() => {
      latestCount.current = count;
      setTimeout(() => {
        console.log(`You clicked ${latestCount.current} times`);
      }, 3000);
    });
    return(
    <div>Count: {count}         <button  onClick={()=>{setCount(count+1)}} >点击我</button>     </div>
    )
}
复制代码

Image [2].png
useRef返回了一个ref对象,它是可变的,其 current属性指向的当前内容,而不是以前的快照。

5、如何设置一个定时器

如今有这样一个需求,进入页面后,须要一个数字不断的加一,离开页面后清除这个定时器,又因为依赖了count,那么很容易想到下面这种写法:async

function Example({
    const [count, setCount] = useState(0)
    useEffect(() => {
        let inter = setInterval(() => {
            setCount(count + 1)
            console.log('count-->', count)
        }, 1000)
        return () => {
            console.log('stopInterval-->', count)
            clearInterval(inter)
        };
    }, [count])
    return (<div>         当前的值是:{count}     </div>)
}
复制代码

发现定时器能正常进行,而且页面能够显示数字: ide

Image [3].png
可是:为何会不断的新建定时器后又销毁呢?这样实在是太不高效了。
Image [4].png
原来是由于 useEffect依赖了 count,当 count改变后又反过来执行 useEffect,其实setCount 还有函数回调方式,不用关心当前值。

function Example({
    const [count, setCount] = useState(0)
    useEffect(() => {
        let inter = setInterval(() => {
            setCount(c => c + 1)
        }, 1000)
        return () => {
            clearInterval(inter)
              };
    }, [])
    return (<div>         当前的值是:{count}     </div>)
}
export default Example
复制代码

6、useReducer

在上一节中,咱们发现更新和动做耦合在一块儿,尤为在一个useEffect依赖多个变量时候。useReduceruseState的替代方案,它接收(state, action) => newState方法,它返回一个数组,相似于useState hook,第一个是当前状态,第二个是dispatch方法。函数

    const [state, dispatch] = useReducer(reducer, initialState, init)
复制代码

利用useReducer能够重写最开始的加法器:

function Example({
    const [count, dispatch] = useReducer(reducer, 0)
    function reducer (state, action{
        return action.type === 'add' ? state + 1 :state - 1
    }
    return (<div>         <button onClick={() => {dispatch({type: 'add'})}}>+1</button>         <button onClick={() => {dispatch({type: 'decrement'})}}>-1</button>         当前的值是:{count}     </div>)
}
复制代码

Image [5].png

7、其余hooks

查看官方文档 自定义Hook其余Hook

8、使用自定义Hook封装axios

  1. 首先利用自定义Hooks 实现一个fetchApi,传入一个axios和参数params,返回结果data
function useFetch(fetch, params{
    const [data, setData] = useState({});
    const fetchApi = useCallback(async () => {
        const res = await fetch(params);
        if (res.code === 1) {
            setData(res.data);
        }
    }, [fetch, params]);
    useEffect(() => {
        fetchApi();
    }, [fetchApi]);
    return data;
}
复制代码

提供一个axios方法fetch

export const fetch = params => {
    const url = 'baseUrl' + (params);
    return axios(url).then(res => res.json());
};
复制代码

去使用这个useFetch

function Demo({
    const data = useFetch(fetch, { params1"xxx" }
    return <div>获得返回值:{data}</div>;
}
复制代码

结果发现,这样并不行得通,一会儿程序就会陷入死循环,好好分析一下,原来params是一个对象,usecallback会误认为每次params都改变了,致使陷入了一个死循环中。:render -->useEffect-->useCllback--->data改变--->render,那么天然而然就会想到useMemo,它使得数据项真正的改变时候才会使得useCallback从新执行。或者使用JSON.stringify转换params 便可。

params = useMemo(() => (params), []);
复制代码

这里有个较为完整的例子,如何使用hooks分页请求表格数据:分页请求

相关文章
相关标签/搜索