React Hooks的实现原理和最佳实践

在谈 react hooks 以前

React的组件化给前端开发带来了史无前例的体验,咱们能够像玩乐高玩具同样将一个组件堆积拼接起来,就组成了一个完整的UI界面,在加快了开发速度的同时又提升了代码的可维护性。可是随着业务功能复杂度提升,业务代码不得不和生命周期函数糅合到一块儿。这样不少重复的业务逻辑代码很难被抽离出来,为了快速开发不得不Ctrl+C,若是业务代码逻辑发生变化时,咱们又不得不一样时修改多个地方,极大的影响开发效率和可维护性。为了解决这个业务逻辑复用的问题,React官方也作了不少努力:javascript

React.mixin

React mixin 是经过React.createClass建立组件时使用的,如今主流都是经过ES6方式建立react组件,官方由于mixin很差追踪变化以及影响性能,因此放弃了对其支持,同时也不推荐咱们使用。这里就简单介绍下mixin:html

mixin的原理其实就是将[mixin]里面的方法合并到组件的prototype上前端

var logMixin = {
    alertLog:function(){
        alert('alert mixin...')
    },
    componentDidMount:function(){
        console.log('mixin did mount')
    }
}

var MixinComponentDemo = React.createClass({
    mixins:[logMixin],
    componentDidMount:function(){
        document.body.addEventListener('click',()=>{
            this.alertLog()
        })
        console.log('component did mount')
    }
})

// 打印以下
// component did mount
// mixin did mount
// 点击页面
// alert mixin...
复制代码

能够看出来mixin就是将logMixn的方法合并到MixinComponentDemo组件中,若是有重名的生命周期函数都会执行(render除外),若是有重名的函数会报错。可是因为mixin的问题比较多因此这里就不展开讲。点击了解更多java

高阶组件

组件是 React 中代码复用的基本单元。但你会发现某些模式并不适合传统组件。react

例如:咱们有个计时器和日志记录组件ios

class LogTimeComponent extends React.Component{
    constructor(props){
        super(props);
        this.state = {
            index: 0
        }
        this.show = 0;
    }
    componentDidMount(){
        this.timer = setInterval(()=>{
            this.setState({
                index: ++index
            })
        },1000)
        console.log('组件渲染完成----')
    }
    componentDidUpdate(){
        console.log(`我背更新了${++this.show}`)
    }
    componentWillUnmount(){
        clearInterval(this.timer)
        console.log('组件即将卸载----')
    }
    render(){
        return(
            <div> <span>{`我已经显示了:${this.state.index}s`}</span> </div>
        )
        
    }
}
复制代码

上面就简单的实现了简单的日志和计时器组件。那么问题来了假若有三个组件分别是LogComponent(须要记录日志)、SetTimeComponent(须要记录时间)、LogTimeShowComponent(日志和时间都须要记录);怎么处理呢?把上面逻辑 Ctrl+C 而后 Ctrl+V 吗?若是记录日志的文案改变须要每一个组件都修改么?官方给咱们提供了高阶组件(HOC)的解决方案:git

function logTimeHOC(WrappedComponent,options={time:true,log:true}){
    return class extends React.Component{
        constructor(props){
            super(props);
            this.state = {
                index: 0
            }
            this.show = 0;
        }
        componentDidMount(){
            options.time&&this.timer = setInterval(()=>{
                this.setState({
                    index: ++index
                })
            },1000)
            options.log&&console.log('组件渲染完成----')
        }
        componentDidUpdate(){
            options.log&&console.log(`我背更新了${++this.show}`)
        }
        componentWillUnmount(){
            this.timer&&clearInterval(this.timer)
            options.log&&console.log('组件即将卸载----')
        }
        render(){
            return(<WrappedComponent {...this.state} {...this.props}/>) } } } 复制代码

logTimeHOC就是一个函数,接受一个组件返回一个新的组件(其实高阶组件就是一个函数)。咱们用这个高阶组件来构建咱们上面的三个组件:github

LogComponent:打印日志组件redux

class InnerLogComponent extends React.Component{
    render(){
        return(
            <div>我是打印日志组件</div>
        )
    }
}
// 使用高阶组件`logTimeHOC`包裹下 
export default logTimeHOC(InnerLogComponent,{log:true})
复制代码

SetTimeComponent:计时组件axios

class InnerSetTimeComponent extends React.Component{
    render(){
        return(
            <div> <div>我是计时组件</div> <span>{`我显示了${this.props.index}s`}</span> </div>
        )
    }
}
// 使用高阶组件`logTimeHOC`包裹下 
export default logTimeHOC(InnerSetTimeComponent,{time:true})
复制代码

LogTimeShowComponent:计时+打印日志组件

class InnerLogTimeShowComponent extends React.Component{
    render(){
        return(
            <div> <div>我是日志打印+计时组件</div> </div>
        )
    }
}
// 使用高阶组件`logTimeHOC`包裹下 
export default logTimeHOC(InnerLogTimeShowComponent)
复制代码

这样不只复用了业务逻辑提升了开发效率,同时还方便后期维护。固然上面的案例只是为了举例而写的案例,实际场景咱们须要本身去合理抽取业务逻辑。高阶组件虽然很好用可是也有一些自身的缺陷:

  • 高阶组件的props都是直接透传下来,没法确实子组件的props的来源。
  • 可能会出现props重复致使报错。
  • 组件的嵌套层级太深。
  • 会致使ref丢失。

React Hooks

上面说了不少,无非就是告诉咱们已经有解决功能复用的方案了。为啥还要React Hooks这个呢?上面例子能够看出来,虽然解决了功能复用可是也带来了其余问题。由此官方给我带来React Hooks,它不只仅解决了功能复用的问题,还让咱们以函数的方式建立组件,摆脱Class方式建立,从而没必要在被this的工做方式困惑,没必要在不一样生命周期中处理业务。

import React,{ useState, useEffect } from 'react'
function useLogTime(data={log:true,time:true}){
    const [count,setCount] = useState(0);
    useEffect(()=>{
        data.log && console.log('组件渲染完成----')
        let timer = null;
        if(data.time){
            timer = setInterval(()=>{setCount(c=>c+1)},1000)
        } 
        return ()=>{
            data.log && console.log('组件即将卸载----')
            data.time && clearInterval(timer)
        }
    },[])
    return {count}
}
复制代码

咱们经过React Hooks的方式从新改写了上面日志时间记录高阶组件。若是不了解React Hooks的基本用法建议先阅读react hook文档。若是想深刻了解setInterval在Hook中的表现能够看这篇从新 Think in Hooks

假设咱们已经掌握了React Hooks,那么我来重写下上面的三个组件:

LogComponent:打印日志组件

export default function LogComponent(){
    useLogTime({log:true})
    return(
        <div>我是打印日志组件</div>
    )
}  
复制代码

SetTimeComponent:计时组件

export default function SetTimeComponent (){
    const {count} = useLogTime({time:true})
    return(
        <div> <div>我是计时组件</div> <span>{`我显示了${count}s`}</span> </div>
    )
}
复制代码

LogTimeShowComponent:计时+打印日志组件

export default function LogTimeShowComponent (){
    const {count} = useLogTime()
    return(
        <div> <div>我是日志打印+计时组件</div> <div>{`我显示了${count}s`}</div> </div>
    )
}
复制代码

咱们用React Hooks实现的这个三个组件和高阶组件一比较是否是发现更加清爽,更加PF。将日志打印和记录时间功能抽象出一个useLogTime自定义Hooks。若是其余组件须要打印日志或者记录时间,只要直接调用useLogTime这个自定义Hooks就能够了。是否是有种封装函数的感受。

React Hooks实现原理

若是让咱们来实现一个React Hooks咱们如何实现呢?好像毫无头绪,能够先看一个简单的useState:(这部份内容只是帮咱们更好的理解Hooks工做原理,想了解Hooks最佳实践能够直接查看React 生产应用)

function App(){
        const [count,setCount] = useState(0);
        useEffect(()=>{
            console.log(`update--${count}`)
        },[count])
        return(
            <div> <button onClick={()=>setCount(count+1)}> {`当前点击次数:${count}`} </button> </div>
        )
    }
复制代码
  • 实现setState

上面能够看出来当调用useState时;会返回一个变量和一个函数,其参数时返回变量的默认值。咱们先构建以下的useState函数:

function useState(initVal) {
    let val = initVal;
    function setVal(newVal) {
        val = newVal;
        render(); // 修改val后 从新渲染页面
    }
    return [val, setVal];
}
复制代码

咱们能够在代码中来使用useState--查看demo;

不出意外当咱们点击页面上的按钮时候,按钮中数字并不会改变;看控制台中每次点击都会输出0,说明useState是执行了。因为val是在函数内部被声明的,每次useState都会从新声明val从而致使状态没法被保存,所以咱们须要将val放到全局做用域声明。

let val; // 放到全局做用域
function useState(initVal) {
    val = val|| initVal; // 判断val是否存在 存在就使用
    function setVal(newVal) {
        val = newVal;
        render(); // 修改val后 从新渲染页面
    }
    return [val, setVal];
}
复制代码

修改useState后,点击按钮时按钮就发生改变了--修改后Demo

  • 实现useEffect

useEffect是一个函数,有两个参数一个是函数,一个是可选参数-数组,根据第二个参数中是否有变化来判断是否执行第一个参数的函数:

// 实现初版 不考虑第二个参数
function useEffect(fn){
    fn();
}
复制代码

ok!不考虑第二个参数很简单,其实就是执行下函数--这里查看Demo(控制台中能看到useEffect执行了)。可是咱们须要根据第二个参数来判断是否执行,而不是一直执行。因此咱们还须要有一个判断逻辑去执行函数。

let watchArr; // 为了记录状态变化 放到全局做用域
function useEffect(fn,watch){
    // 判断是否变化 
    const hasWatchChange = watchArr?
    !watch.every((val,i)=>{ val===watchArr[i] }):true;
    if( hasWatchChange ){
        fn();
        watchArr = watch;
    }
}
复制代码

完成好useEffect咱们在去测试下 --测试demo

打开测试页面咱们每次点击按钮,控制台会打印当前更新的count;到目前为止,咱们模拟实现了useStateuseEffect能够正常工做了。不知道你们是否还记得咱们经过全局变量来保证状态的实时更新;若是组件中要屡次调用,就会发生变量冲突的问题,由于他们共享一个全局变量。如何解决这个问题呢?

  • 解决同时调用多个 useState useEffect的问题
// 经过数组维护变量
let memoizedState  = [];
let currentCursor = 0;

function useState(initVal) {
    memoizedState[currentCursor] = memoizedState[currentCursor] || initVal;
    function setVal(newVal) {
        memoizedState[currentCursor] = newVal;
        render(); 
    }
    // 返回state 而后 currentCursor+1
    return [memoizedState[currentCursor++], setVal]; 
}

function useEffect(fn, watch) {
  const hasWatchChange = memoizedState[currentCursor]
    ? !watch.every((val, i) => val === memoizedState[currentCursor][i])
    : true;
  if (hasWatchChange) {
    fn();
    memoizedState[currentCursor] = watch;
    currentCursor++; // 累加 currentCursor
  }
}
复制代码

修改核心是将useState,useEffect按照调用的顺序放入memoizedState中,每次更新时,按照顺序进行取值和判断逻辑--查看Demo

  • 图解React Hooks 原理

图解Reat Hooks

如上图咱们根据调用hooks顺序,将hooks依次存入数组memoizedState中,每次存入时都是将当前的currentcursor做为数组的下标,将其传入的值做为数组的值,而后在累加currentcursor,因此hooks的状态值都被存入数组中memoizedState

图解React Hooks执行更新

上面状态更新图,咱们能够看到执行setCount(count + 1)setData(data + 2)时,先将旧数组memoizedState中对应的值取出来从新复值,从而生成新数组memoizedState。对因而否执行useEffect经过判断其第二个参数是否发生变化而决定的。

这里咱们就知道了为啥官方文档介绍:不要在循环,条件或嵌套函数中调用 Hooks, 确保老是在你的 React 函数的最顶层调用他们。由于咱们是根据调用hooks的顺序依次将值存入数组中,若是在判断逻辑循环嵌套中,就有可能致使更新时不能获取到对应的值,从而致使取值混乱。同时useEffect第二个参数是数组,也是由于它就是以数组的形式存入的。

固然,react官方不会像咱们这么粗暴的方式去实现的,想了解官方是如何实现能够去这里查看

React 生产应用

在说到React实际工做应用以前,但愿你能对React Hooks有作过了解,知道如useStateuseEffectuseContext等基本Hooks的使用,以及如何自定义Hooks,若是不了解能够点击这里了解关于Hook的知识点

如何模拟React的生命周期

  • constructor:函数组件不须要构造函数。你能够经过调用 useState 来初始化 state。
  • componentDidMount:经过 useEffect 传入第二个参数为[]实现。
  • componentDidUpdate:经过 useEffect 传入第二个参数为空或者为值变更的数组。
  • componentWillUnmount:主要用来清除反作用。经过 useEffect 函数 return 一个函数来模拟。
  • shouldComponentUpdate:你能够用 React.memo 包裹一个组件来对它的 props 进行浅比较。来模拟是否更新组件。
  • componentDidCatch and getDerivedStateFromError:目前尚未这些方法的 Hooks 等价写法,但很快会加上。

如何经过React Hooks进行数据请求

前端页面免不了要和数据打交道,在Class组件中咱们一般都是在componentDidMount生命周期中发起数据请求,然而咱们使用Hooks时该如何发送请求呢?

import React,{ useState,useEffect } from 'react';

export default function App() {
  const [data, setData] = useState(null);
  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        "https://easy-mock.com/mock/5b514734fe14b078aee5b189/example/queryList"
      );
      setData(result.data); // 赋值获取后的数据
    };
    fetchData();
  });

  return (
    <div> {data ? ( <ul> <li>{`id:${data.id}`}</li> <li>{`title:${data.title}`}</li> </ul> ) : null} </div>
  );
}
复制代码

能够查看Demo,咱们发现页面报错。根据咱们了解到的知识,若是 useEffect 第二个参数不传入,致使每次data更新都会执行,这样就陷入死循环循环了。咱们须要改造下

...

useEffect(() => {

    ...

},[]);

'''
复制代码

咱们给第二个参数加上一个[]发现页面就能够显示了,将这个Demo中注释解除了。,咱们就能够发现页面正常显示了。

咱们一个程序会有多个组件,不少组件都会有请求接口的逻辑,不能每一个须要用到这个逻辑的时候都从新写或者Ctrl+C。因此咱们须要将这个逻辑抽离出来做为一个公共的Hooks来调用,那么咱们就要用到自定义Hooks。

// config => 指望格式
// {
// method: 'post',
// url: '/user/12345',
// data: {
// firstName: 'Fred',
// lastName: 'Flintstone'
// }
// }
function useFetchHook(config){
    const [data,setData] = useState(null);
    useEffect(() => {
        const fetchData = async () => {
            const result = await axios(config);
            setData(result.data)
        };
        fetchData();
    },[]);
    return { data }
}
复制代码

如今咱们就将请求接口的逻辑单独抽出来了,若是那个组件须要使用能够直接引入useFetchHook这里能够查看Demo

上面的useFetchHook虽然能够解决咱们请求接口的问题;若是咱们如今是一个分页接口,每次传入不一样的page都会从新请求,因此咱们还须要修改下:

// watch => 指望格式是 []
function useFetchHook(config,watch){
    const [data,setData] = useState(null);
    useEffect(() => {
        ...
    },
    watch?[...watch]:[] // 判断是否有须要监测的属性
    );
    return { data }
}
复制代码

点击查看Demo,咱们如今点检页面上的按钮发现页面的数据户一直发生变化,控制台也会打印,说明咱们更改page时都会从新请求接口,上面的问题就解决了。

上面的useFetchHook虽然能够解决大部分状况,可是一个健全的 接口请求Hooks 还须要告知使用者接口请求状态的成功、失败。咱们继续---

function useFetchHook(config,watch){
    // status 标识当前接口请求状态 0:请求中 1:请求成功 2:请求失败
    const [status,setStatus] = useState(0);
    const [data,setData] = useState(null);
    useEffect(() => {
        try{
            ...
            setStatus(1) // 成功
        }catch(err){
            setStatus(2) // 失败
        }
    },
    watch?[...watch]:[] // 判断是否有须要监测的属性
    );
    return { data, status }
}
复制代码

点击这里能够查看;咱们改造后发现页面按钮多了接口状态,点击时也会发生改变,为了测试失败状态,咱们将 Chrome - network - Offine 改成 offine状态,再次点击发现状态就变成2(失败)。

尚未完呢!使用者知道了状态后能够作相应的 loading... 操做等等;可是对于接口的报错咱们也能够作一个埋点信息或者给一个友善的提示---至于后面怎么写我相信你们均可以发挥本身的想象。下面是useFetchHook完整代码:

function useFetchHook(config, watch) {
    const [data, setData] = useState(null);
    const [status, setStatus] = useState(0);
    useEffect(
        () => {
        const fetchData = async () => {
            try {
            const result = await axios(config);
            setData(result.data);
            setStatus(1);
            } catch (err) {
            setStatus(2);
            }
        };

        fetchData();
        },
        watch ? [watch] : []
    );
    return { data, status };
}
复制代码

提升性能的操做

class App extends Component{
    render() {
        return 
        <div> <Button onClick={ () => { console.log('do something'); }} /> </div>; } } 复制代码

上面App组件若是props发生改变时,就会从新渲染组件。若是这个修改并不涉及到Button组件,可是因为每次render的时候都会产生新的onClick函数,react就认为其发生了改变,从而产生了没必要要的渲染而引发性能浪费。

class App extends Component{
    constructor(){
        super();
        this.buttonClick = this.buttonClick.bind(this);
    }
    render() {
        return 
        <div> <Button onClick={ this.buttonClick } /> </div>; } } 复制代码

在类组件中咱们能够直接将函数绑定到this对象上。在Hooks组件中怎么解决呢?

function App(){
    const buttonClick = useCallback(
        () => { console.log('do something'),[]
    )
    return(
        <div> <Button onClick={ buttonClick } /> </div> ) } 复制代码

如上直接用useCallback生成一个记忆函数,这样更新时就不会发生渲染了。在react Hooks 中 还有一个useMemo也能实现一样的效果。

React Hooks 实现一个简版的redux

React是从上而下的单向数据流,父子组件之间信息传递能够经过Props实现,兄弟组件的信息传递咱们能够将Props提高到共同的父级实现信息传递,若是组件层级嵌套过深,对开发者来讲是十分痛苦的。因此社区基于redux产生了react-redux工具,固然咱们这里对react-redux作讲解,而是提供一种新的解决方案。

这里提供体验地址

// 建立Context
const AppContext = React.createContext();
const AppDispatch = (state, action) => {
    switch (action.type) {
        case "count.add":
            return { ...state, count: state.count + 1 };
        case "count.reduce":
            return { ...state, count: state.count - 1 };
        case "color":
            return { ...state, color: colorArr[getRandom()] };
        default:
            return state;
    }
};
// 建立Provider
const AppProvider = props => {
    let [state, dispatch] = useReducer(AppDispatch, context);
    return (
        <AppContext.Provider value={{ state, dispatch }}> {props.children} </AppContext.Provider> ); }; // ... function Demo3() { // 使用 Context const { state, dispatch } = useContext(AppContext); return ( <div className="demo" style={{ backgroundColor: state.color }} onClick={() => { dispatch({ type: "count.add" }); dispatch({ type: "color" }); }} > <div className="font">{state.count}</div> </div> ); } // ... // 将 AppProvider 放到根组件 ReactDOM.render( <AppProvider> <App /> </AppProvider>, rootElement ); 复制代码

完整代码在这里获取

一块儿来封装经常使用的Hooks

在开始封装经常使用Hooks以前插一个题外话,咱们在开发中时,不可能都是新项目;对于那些老项目(react已经升级到16.8.x)咱们应该如何去使用Hooks呢?其实很简单咱们能够开发一些经常使用的hooks,当咱们老项目有新的功能咱们彻底能够用Hooks去开发,若是对老的组件进行修改时咱们就能够考虑给老组件上Hooks;不建议一上来就进行大改。随着咱们的经常使用Hooks组件库的丰富,后期改起来也会很是快。

在使用Hooks时不免少不了一些经常使用的Hooks,若是能够将这些经常使用的Hooks封装起来岂不是美滋滋!

首先能够建立以下目录结构:

'React Hooks 目录结构'

index.js文件

import useInterval from  './useInterval'
// ...
export{
    useInterval
    // ...
}
复制代码

lib中存放经常使用Hooks, 如实现一个useInterval:为啥咱们须要一个useInterval的自定义Hooks呢?

在程序中直接使用 setInterval

function App(){
    const [count,setCount] = useState(0);
    useEffect(()=>{
        console.log(count)
        setInterval(()=>{
            setCount(count+1)
        })
    })
    return <p>{count}</p>
}
复制代码

上面代码直接运行咱们会发现页面上的 count 越加越快,是因为 count 每次发生改变都致使定时器触发。因此须要每次在清除下定时器:

function App(){
    const [count,setCount] = useState(0);
    useEffect(()=>{
        console.log(count)
        const timer = setInterval(()=>{
            setCount(count+1)
        })
        // 清除反作用
        return ()=>{ clearInterval(timer) } 
    })
    return <p>{count}</p>
}
复制代码

改动代码后页面好像能够正常显示了,咱们打开控制台能够看到一直会打印 count ,这样对于性能来将无疑是一种浪费,咱们只须要执行一次就能够了,在改下代码

function App(){
    const [count,setCount] = useState(0);
    useEffect(()=>{
        console.log(count)
        const timer = setInterval(()=>{
            setCount(count+1)
        })
        return ()=>{ clearInterval(timer) }
    },[]) // 添加第二个参数
    return <p>{count}</p>
}
复制代码

在看页面,发现控制台好像是只打印一次了,可是页面上的 count 以及不发生改变了,这不是咱们想要的,还须要改变下:

function App(){
    const [count,setCount] = useState(0);
    useEffect(()=>{
        console.log(count)
        const timer = setInterval(()=>{
            setCount(count+1)
        })
        return ()=>{ clearInterval(timer) }
    },[count]) // 添加 count 变量 
    return <p>{count}</p>
}
复制代码

Ok!如今好像解决了上面的问题了,可是这个只是一个定时器累加的任务并且只涉及到一个变量,若是是定时执行其余任务,同时有多个变量,那么岂不是又要修改。因此为了解决这个问题咱们迫切须要一个useInterval自定义钩子。

function useInterval(callback,time=300){
    const intervalFn = useRef(); // 1
    useEffect(()=>{
        intervalFn.current = callback;  // 2
    })
    useEffect(()=>{
        const timer = setInterval(()=>{
            intervalFn.current()
        },time)
        return ()=>{ clearInterval(timer) }
    },[time])  // 3
}
复制代码

自定义useInterval钩子体验地址

简单介绍下useInterval钩子: 1.经过useRef建立一个对象;2.将须要执行的定时任务储存在这个对象上;3.将time做为第二个参数是为了当咱们动态改变定时任务时,能太重新执行定时器。

开发中使用useInterval以下:

useInterval(() => {
    // you code
}, 1000);
复制代码

是否是很简单有很方便,如今将useInterval放到lib文件夹中,再在index.js文件中导出一下,其余地方要用的时候直接import就能够了。

开放思惟

问题:作一个useImgLazy的 hooks 函数。

为提升网页的性能咱们通常都会网页上图片资源作一些优化,懒加载就是一种方案,useImgLazy就是实现懒加载的 Hooks

// 判断是否在视口里面
function isInWindow(el){
    const bound = el.getBoundingClientRect();
    const clientHeight = window.innerHeight;
    return bound.top <= clientHeight + 100;
}
// 加载图片真实连接
function loadImg(el){
    if(!el.src){
        const source = el.getAttribute('data-sourceSrc');
        el.src = source;
    }
}
// 加载图片
function checkImgs(className){
    const imgs = document.querySelectorAll(`img.${className}`);
    Array.from(imgs).forEach(el =>{
        if (isInWindow(el)){
            loadImg(el);
        }
    })
}
function useImgLazy(className){
    useEffect(()=>{
        window.addEventListener('scroll',()=>{
            checkImgs(className)
        });
        checkImgs(className);

        return ()=>{
            window.removeEventListener('scroll')
        }
    },[])
}
复制代码

上面代码逻辑就是 经过getBoundingClientRect获取图片元素的位置,从而判断是否显示图片真实地址,用useEffect模拟页面加载成功(onload事件)同时监听scroll事件。

在须要使用图片懒加载的项目中使用:

function App(){
    // ...
    useImgLazy('lazy-img')
    // ...
    return (
        <div> // ... <img className='lazy-img' data-sourceSrc='真实图片地址'/> </div> ) } 复制代码

以上useImgLazy代码我是写这篇文章时忽然诞生的一个想法,没有验证,若是哪位同窗验证后有问题还请告知我在这里是反馈问题,如生产上使用产生问题我一律不负责。

我相信你们看了这篇文章必定会蠢蠢欲动,建立一个自定义 Hooks 。点击这里大家使用过哪些自定义Hooks函数你能够分享、学习其余人是如何自定义有趣的Hooks。

这里能够分享Hooks的最佳实践帮助咱们更快的使用React Hooks说说Hooks中的一些最佳实践##

这里会收集你们分享的--实用的有趣的Hooks

参考文章

相关文章
相关标签/搜索