终于搞懂 React Hooks了!!!!!
发布于 4 年前 作者 banyungong 2749 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

学习总结!

从三个纬度学习记录:

  1. 为什么🤔️
  2. 怎么用🔨
  3. 知识点⛽️

useState

为什么要使用useState?

useState 的出现是 : 在函数组件里面使用 class的setState,刺激!

解决了的问题是:当我们一个函数组件想要有自己维护的state的时候,不得已只能转换成class。这样很麻烦!

如何使用useState?

const [name, setName] = useState('rose')

useState踩坑知识点

😄1. 重点: useState的初始值,只在第一次有效

我当时反正没有当回事,直到遇到了坑...

🌰2. 举个例子:

当我点击按钮修改name的值的时候,我发现在Child组件, 是收到了,但是并没有通过useState赋值给name!

结论: 实践检验知识点!😭

const Child = memo(({data}) =>{
    console.log('child render...', data)
    const [name, setName] = useState(data)
    return (
        <div>
            <div>child</div>
            <div>{name} --- {data}</div>
        </div>
    );
})

const Hook =()=>{ console.log(‘Hook render…’) const [count, setCount] = useState(0) const [name, setName] = useState(‘rose’)

<span class="hljs-built_in">return</span>(
    &lt;div&gt;
        &lt;div&gt;
            {count}
        &lt;/div&gt;
        &lt;button onClick={()=&gt;<span class="hljs-built_in">set</span>Count(count+1)}&gt;update count &lt;/button&gt;
        &lt;button onClick={()=&gt;<span class="hljs-built_in">set</span>Name(<span class="hljs-string">'jack'</span>)}&gt;update name &lt;/button&gt;
        &lt;Child data={name}/&gt;
    &lt;/div&gt;
)

}

useEffect

为什么要使用useEffect?

useEffect 的出现是 : 在函数组件里面使用 class的生命周期函数,还是所有函数的合体!刺激!

如何使用useEffect?

useEffect(()=>{
    ...
})

useEffect知识点合集

😄1.只在第一次使用的componentDidMount,可以用来请求异步数据...、

useEffect最后,加了[]就表示只第一次执行

useEffect(()=>{
    const users = 获取全国人民的信息()
},[])

😄2.用来替代willUpdate等每次渲染都会执行的生命函数

useEffect最后,不加[]就表示每一次渲染都执行

useEffect(()=>{
    const users = 每次都获取全国人民的信息()
})

😄3.每次渲染都执行感觉有点费,所以:

useEffect最后,加[],并且[]里面加的字段就表示,这个字段更改了,我这个effect才执行

useEffect(() => {
    const users = (name改变了我才获取全国人民的信息())
},[name])

😄4.如果我想要分别name和age呢:

可以写多个useEffect

useEffect(() => {
    const users = (name改变了我才获取全国人民的name信息())
},[name])

useEffect(() => { const users = (name改变了我才获取全国人民的age信息()) },[age])

😄5.如果我们之前订阅了什么,最后在willUnMount这个生命周期里面要取消订阅,这可咋用useEffect实现啊:

在effect的return里面可以做取消订阅的事

useEffect(() => {
    const subscription = 订阅全国人民吃饭的情报!
    return () => {
        取消订阅全国人民吃饭的情报!
    }
},[])

为什么要取消订阅?

大家都知道,render了之后会执行重新useEffect,如果useEffect里面有一个每setInterval...那么每次render了,再次执行useEffect就会再创建一个setInterval,然后就混乱了...可以把下面案例return的内容删掉感受下

useEffect(() => {
        console.log('use effect...',count)
        const timer = setInterval(() => setCount(count +1), 1000)
        return ()=> clearInterval(timer)
    })

😄6.useEffect的一些暗戳戳的规则:

1.useEffect 里面使用到的state的值, 固定在了useEffect内部, 不会被改变,除非useEffect刷新,重新固定state的值

 const [count, setCount] = useState(0)
    useEffect(() => {
        console.log('use effect...',count)
        const timer = setInterval(() => {
            console.log('timer...count:', count)
            setCount(count + 1)
        }, 1000)
        return ()=> clearInterval(timer)
    },[])

2.useEffect不能被判断包裹

const [count, setCount] = useState(0)
if(2 < 5){
   useEffect(() => {
        console.log('use effect...',count)
        const timer = setInterval(() => setCount(count +1), 1000)
        return ()=> clearInterval(timer)
    }) 
}

3.useEffect不能被打断

const [count, setCount] = useState(0)
useEffect(...)

return // 函数提前结束了

useEffect(…) }

具体原因跟到useEffect的生成执行规则有关系:看文档去!

useRef

为什么要使用useRef?

前面提到的:

useEffect 里面使用到的state的值, 固定在了useEffect内部, 不会被改变,除非useEffect刷新,重新固定state的值

 const [count, setCount] = useState(0)
    useEffect(() => {
        console.log('use effect...',count)
        const timer = setInterval(() => {
            console.log('timer...count:', count)
            setCount(count + 1)
        }, 1000)
        return ()=> clearInterval(timer)
    },[])

useEffect里面的state的值,是固定的,这个是有办法解决的,就是用useRef,可以理解成useRef的一个作用:

就是相当于全局作用域,一处被修改,其他地方全更新...

如何使用useRef?

const countRef = useRef(0)

useRef知识点合集

😄 1. 就是相当于全局作用域,一处被修改,其他地方全更新...

 const [count, setCount] = useState(0)
 const countRef = useRef(0)
useEffect(() => {
    console.log('use effect...',count)
    const timer = setInterval(() => {
        console.log('timer...count:', countRef.current)
        setCount(countRef.current + 1)
    }, 1000)
    return ()=> clearInterval(timer)
},[])

😄 2. 普遍操作,用来操作dom

const btnRef = useRef(null)

<button>click me </button>

活学活用,记得取消绑定事件哦! return ()=> btnRef.current.removeEventListener('click',onClick, false)

const Hook =()=>{
    const [count, setCount] = useState(0)
    const btnRef = useRef(null)
useEffect(() =&gt; {
    console.log(<span class="hljs-string">'use effect...'</span>)
    const onClick = ()=&gt;{
        <span class="hljs-built_in">set</span>Count(count+1)
    }
    btnRef.current.addEventListener(<span class="hljs-string">'click'</span>,onClick, <span class="hljs-literal">false</span>)
    <span class="hljs-built_in">return</span> ()=&gt; btnRef.current.removeEventListener(<span class="hljs-string">'click'</span>,onClick, <span class="hljs-literal">false</span>)
},[count])

<span class="hljs-built_in">return</span>(
    &lt;div&gt;
        &lt;div&gt;
            {count}
        &lt;/div&gt;
        &lt;button ref={btnRef}&gt;click me &lt;/button&gt;
    &lt;/div&gt;
)

}

useMemo

为什么要使用useMemo?

举个🌰:

const Child = memo(({data}) =>{
    console.log('child render...', data.name)
    return (
        <div>
            <div>child</div>
            <div>{data.name}</div>
        </div>
    );
})

const Hook =()=>{ console.log(‘Hook render…’) const [count, setCount] = useState(0) const [name, setName] = useState(‘rose’)

const data = {
    name
}

<span class="hljs-built_in">return</span>(
    &lt;div&gt;
        &lt;div&gt;
            {count}
        &lt;/div&gt;
        &lt;button onClick={()=&gt;<span class="hljs-built_in">set</span>Count(count+1)}&gt;update count &lt;/button&gt;
        &lt;Child data={data}/&gt;
    &lt;/div&gt;
)

}

当我们点击按钮更新count的时候,Effect组件会render,一旦render, 执行到这一行代码:

   const data = {
        name
    }

这一行代码会生成有新的内存地址的对象,那么就算带着memo的Child组件,也会跟着重新render, 尽管最后其实Child使用到的值没有改变!

这样就多余render了,感觉性能浪费了!于是useMemo 作为一个有着暂存能力的,就来了。

如何使用useMemo?

  const data = useMemo(()=>{
        return {
            name
        }
    },[name])

的时候,就会先根据[name]里面的name值判断一下,因为useMemo 作为一个有着暂存能力的,暂存了上一次的name结果。

结果一对比上一次的name,我们发现name值居然没有改变!那么这次data就不重新赋值成新的对象了!

没有新的对象,就没有新的内存地址,那么Child就不会重新render!

const Child = memo(({data}) =>{
    console.log('child render...', data.name)
    return (
        <div>
            <div>child</div>
            <div>{data.name}</div>
        </div>
    );
})

const Hook =()=>{ console.log(‘Hook render…’) const [count, setCount] = useState(0) const [name, setName] = useState(‘rose’)

const data = useMemo(()=>{ return { name } },[name])

<span class="hljs-built_in">return</span>(
    &lt;div&gt;
        &lt;div&gt;
            {count}
        &lt;/div&gt;
        &lt;button onClick={()=&gt;<span class="hljs-built_in">set</span>Count(count+1)}&gt;update count &lt;/button&gt;
        &lt;Child data={data}/&gt;
    &lt;/div&gt;
)

}

useMemo知识点合集

useMemo 一看 就感觉跟到memo有种蜜汁关系,因为都有memo...

😄 1. 首先,memo 的用法是:函数组件里面的PureComponent

但是,如果函数组件被 React.memo 包裹,且其实现中拥有 useState 或 useContext 的 Hook,当 context 发生变化时,它仍会重新渲染。

😄 2. 而且,memo是浅比较,意思是,对象只比较内存地址,只要你内存地址没变,管你对象里面的值千变万化都不会触发render

😄 3. 最后,useMemo 的作用是, 于是useMemo 作为一个有着暂存能力的,就来了:

useCallback

为什么要使用useCallback?

useMemo 解决了值的缓存的问题,那么函数呢?

下面这个🌰就是,当点击count的按钮,Effect组件render,遇到了:

   const onChange=(e)=>{
        setText(e.target.value())
   }

则,重新生成了一个onChange函数,赋值给了Child组件,浅比较失败,Child组件成功重新render,尽管Child组件什么都没有做!

const Child = memo(({data, onChange}) =>{
    console.log('child render...')
    return (
        <div>
            <div>child</div>
            <div>{data}</div>
            <input type="text" onChange={onChange}/>
        </div>
    );
})

const Hook =()=>{
    console.log('Hook render...')
    const [count, setCount] = useState(0)
    const [name, setName] = useState('rose')
    const [text, setText] = useState('')

   const onChange=(e)=>{
        setText(e.target.value())
   }
    
    return(
        <div>
            <div>count: {count}</div>
            <div>text : {text}</div>
            <button onClick={()=>setCount(count + 1)}>count + 1</button>
            <Child data={name} onChange={onChange}/>
        </div>
    )
}

如何使用useCallback?

   const onChange = useCallback((e)=>{
        setText(e.target.value())
   },[])

useCallback 知识点合集

😄1.useMemouseCallback 类似,都是有着缓存的作用。本质的区别可能就是:

useMemo 是缓存值的

useCallback 是缓存函数的

😄2.没有依赖,添加空的依赖,就是空数组!

useReducer

为什么要使用useReducer?

顾名思义,useReducer就是 class 里面那个reducer

如何使用useReducer?

举个🌰:

const reducer =(state = 0, {type})=>{
    switch (type) {
        case "add":
            return state+1
        case 'delete':
            return state-1
        default:
            return state;
    }
}

const Hook =()=>{ const [count, dispatch] = useReducer(reducer, 0) return( <div> count:{count} <button onClick={()=> dispatch({type:‘add’})}>add</button> <button onClick={()=> dispatch({type:‘delete’})}>delete</button> </div> ) }

export default Hook

useReducer知识点合集

暂无特别的...😼

useContext

为什么要使用useContext?

useContext 就是 class 里面的 那个 context。

如何使用useContext?

import React, {useContext, useReducer} from 'react'

const reducer = (state = 0, {type}) => { switch (type) { case “add”: return state + 1 case ‘delete’: return state - 1 default: return state; } } const Context = React.createContext(null);

const Child = () => { const [count, dispatch] = useContext(Context) return ( <div> <div>child…{count}</div> <button onClick={() => dispatch({type: ‘add’})}>child add</button> <button onClick={() => dispatch({type: ‘delete’})}>child delete</button> </div>

)

}

const Hook = () => { const [count, dispatch] = useReducer(reducer, 10) return ( <Context.Provider value={[count, dispatch]}> <div> <div>mom … {count}</div> <Child/> <button onClick={() => dispatch({type: ‘add’})}>mom add</button> <button onClick={() => dispatch({type: ‘delete’})}>mom delete</button> </div> </Context.Provider> ) }

export default Hook

useContext知识点合集

暂无特别的...😼

自定义hook!

自定义一个当resize 的时候 监听window的width和height的hook

import {useEffect, useState} from "react";

export const useWindowSize = () => { const [width, setWidth] = useState() const [height, setHeight] = useState()

useEffect(() =&gt; {
    const {clientWidth, clientHeight} = document.documentElement
    <span class="hljs-built_in">set</span>Width(clientWidth)
    <span class="hljs-built_in">set</span>Height(clientHeight)
}, [])

useEffect(() =&gt; {
    const handleWindowSize = () =&gt;{
        const {clientWidth, clientHeight} = document.documentElement
        <span class="hljs-built_in">set</span>Width(clientWidth)
        <span class="hljs-built_in">set</span>Height(clientHeight)
    };

    window.addEventListener(<span class="hljs-string">'resize'</span>, handleWindowSize, <span class="hljs-literal">false</span>)

    <span class="hljs-built_in">return</span> () =&gt; {
        window.removeEventListener(<span class="hljs-string">'resize'</span>,handleWindowSize, <span class="hljs-literal">false</span>)
    }
})

<span class="hljs-built_in">return</span> [width, height]

}

如何使用:

const [width, height] = useWindowSize()

最后

内容来源于网络

原文出处及转载信息见文内详细说明,如有侵权,请联系 vueclub@126.com 删除

回到顶部