React Hooks学习

使用hooks理由

  1. 高阶组件为了复用,导致代码层级复杂
  2. 生命周期的复杂
  3. 写成functional组件,无状态组件 ,因为需要状态,又改成了class,成本高

一、useState (修改状态的钩子函数)

  • 语法:
    const [state, setstate] = useState(initialState)
  • 状态:
    (1) 自带性能优化的机制,记住变量的状态
    每一次修改状态值的时候,会拿最新要修改的值和之前的状态值做比较(基于Object.is做比较);
    如果发现两次的值是一样的,则不会修改状态,也不会让视图更新。
    如果它修改的状态值一直更新,则会一直调用!!
  1. 第一个值是存储状态的值,第二个是改变状态的唯一方法
    • 基本数据类型修改状态
       const [state, setstate] = useState(initialState)
       const [name, setname] = useState('小明')
       setname('小红')
      
    • 复杂数据类型修改状态
       const [list, setList] = useState(["aa","bb","cc"])
       setList([...list,"dd"])  
      
    • 修改状态为函数类型
      const [count, setCount] = useState(0)
      setCount(count => count+1)
      
      注:不建议直接改变原来的状态,而是直接用改变状态的唯一方法去操作
      
    setXxx(newValue): 参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值
    setXxx(value => newValue): 参数为函数,接收原本的状态值,返回新的状态值,内部用其覆盖原来的状态值

二、useCallback (缓存函数)

  • 语法:
    useCallback(()=>{},[deps])
  • 状态:
    (1) 自带性能优化的机制,记住函数的状态
    (2) 缓存创建的方法,防止因为组件的渲染,导致方法的重新创建,只有第二个参数变化了,才重新声明一次。
    let getName = useCallback(()=>{
        console.log(name)
    },[name])
    <button onClick={()=>getName()}>你好</button>
    
  • [deps] 的影响:
    • useCallback(() => {}, [ ] )此种情况,则useCallback函数只执行一次
    • useCallback(() => {} ) 此种情况,则每次都重新声明一次,拿到的都是最新的deps
    • useCallback(() => {},[name] )此种情况, 该参数存在且有deps,当name发生变化,该函数就会重新声明一次

三、useMemo (缓存组件)

  • 语法:
    useMemo(() => () => {},[deps])
  • 状态:
    (1) useMemo会执行第一个函数,并且将执行结果返回。
    (2) useMemo可以取代useCallback

四、useEffect (副作用钩子)

  • 语法:
    • useEffect(() => {}, [deps])
      • deps:依赖,选填
      • 传递空数组,只会执行一次
  • 执行时机:
    在组件渲染完成后执行,在类组件中会把请求放在componentDidMount里面,不会阻塞浏览器的渲染过程。
  • 作用
    • 在类组件中会把请求放在componentDidMount里面
    • 在函数组件中可以使用useEffect()
    • 设置订阅 / 启动定时器
    • 发送ajax请求获取数据
    • 手动更改真实dom
  • [deps] 的影响:
    • useEffect(() => {}, [ ] )此种情况,则effect函数只执行一次
    • useEffect(() => {} ) 此种情况,则useEffect函数监视所有useState的数据,当数据发生改变,则useEffect函数就执行
    • useEffect(() => {},[name] )此种情况, 该参数存在且有deps,当name发生变化,则useEffect函数执行
  • 什么时候 [ ] 里面可以写值?
    • 当修改的值,不止被改变一次的时候
         export default function App() {
           const [name, setname] = useState("王五")
           useEffect(() => {
               setname(name.substring(0,1))
           }, [name])
           // 第一次执行一次, 之后name(依赖)更新也会执行
           return (
               <div>
                   我的名字是-------{name}
                   <button onClick={()=>{
                       setname("张三")
                   }}>click</button>
               </div>
           )
       }
      
  • useEffect 开启定时器后,如何取消定时器?
    const [num,setNum] = useState(1)
    useEffect(() => {
        let timerId = setInterval(() => {
            setNum(num => num+1 )
        }, 1);
        return ()=>{
            clearTimeout(timerId); // 组件卸载时清除定时器
        }
    
        // useEffect 相当于componentDidMount,
        //useEffect函数返回的函数,相当于componentWillunMount
    }, [])
    
  • 总结:
    • 可以把 useEffect Hook 看做如下三个函数的组合
      • componentDidMount ()
      • componentDidUpdate()
      • componentWillUnmount()

五、useLayoutEffect (同步执行副作用)

  • 语法:
    • useLayoutEffect(() => {}, [deps])
      • deps:依赖,选填
      • 传递空数组,只会执行一次
  • 执行时机:
    在组件渲染完成后,浏览器绘制之前执行,可以看作是 useEffect 的同步版本。
    useLayoutEffect 的回调函数在每次渲染结束后同步执行,会阻塞浏览器的渲染过程。
    可能导致页面性能下降
  • 作用
    • 避免页面抖动(在 useEffect 里修改DOM很有可能出现)的话,把操作dom的代码放入 useLayoutEffect ,操作dom的行为和 react 做出的更改,被一次性渲染到屏幕上,只有一次回流、重绘的代价。

六、useRef (保存引用值)

  • 语法:
    先定义ref={myref} , 后使用const myref = useRef()
  • 使用:
    • 表单类用法:
    export default function App() {
        const mytext = useRef()
        const delete = ()=>{
            mytext.current.value = ""
        }
        return (
            <div>
                <input ref={mytext}/>
                <button onClick={delete}>清空</button>
            </div>
        )
    }
    
    • 非表单类用法:
      • 如何保存变量?
    export default function App() {
        var myAge = useRef(18)
        return (
            <button onClick={()=>{ myAge.current ++ }}>
            当前我的年龄为:{myAge}
            </button>
        )
    }
    

七、useContext (一种组件间通信方式)

  • 语法:
    useContext()
  • 使用:
    1. 创建Context容器对象:
    const xxx = React.createContext() 
    
    1. 渲染子组件时,外面包裹xxx.Provider, 通过value属性给后代组件传递数据:
    <xxx.Provider value={数据}>
       子组件
    </xxx.Provider>
    
    1. 后代组件读取数据:
    //第一种方式:仅适用于类组件 
     static contextType = xxxContext  // 声明接收context
     this.context // 读取context中的value数据
      
    //第二种方式: 函数组件与类组件都可以
    
      <xxx.Consumer>
        {
          value => ( // value就是context中的value数据
            要显示的内容
          )
        }
      </xxxContext.Consumer>
    
  • 示例:
    import React, { useState,useEffect,useContext } from 'react'
    
    // 类式组件context用法
    // 爷爷给孙子零花钱和小汽车怎么给?
    
    const GlobalContext  = React.createContext() //创建上下文对象
    const { Provider,Consumer } = GlobalContext //解构
    
    A组件================
     export default class A extends React.Component {
     	state = { money: "10元", car: "兰博基尼" }
        render () {
            const { money, car } = this.state
            return(
                <div className='a'>
                    <h2>我是爷爷</h2>
                    <Provider value={{money:money,car:car}}>
                        <B/>
                    </Provider>
                </div>
            )
        }
    }
    B组件====================
    class B extends A {
        static contextType = GlobalContext
        render  () {
            return(
                <div className='b'>
                    <h2>我是爸爸</h2>
                    <h3>{GlobalContext.money}</h3>
                    <C/>
                </div>
            )
        }    
    }
    C组件=============== 类式写法
    class C extends B {
        static contextType = GlobalContext //谁声明,谁使用。
        render  () {
            return(
                <div className='c'>
                    <h2>我是儿子</h2>
                    <p>我的钱-{this.context.money}</p>
                    <p>我的车-{this.context.car}</p>
                </div>
            )
        }
    }
    
  • 问:若c不是类式组件,怎么接收爷爷的钱和小汽车?
  • 答: (第一种方法Consumer)
     function C(){
           return(
             <div className='c'>
                 <h2>我是儿子</h2>
                 <Consumer>
                     {
                         value=>{
                             return `我的钱有:${value.money},我的车:${value.car}`
                         }
                     }
                 </Consumer>
             </div>
         )
     }
    
  • 答: (第二种方法useContext)
    function C(){
    	const value = useContext(GlobalContext)
       	return(
          <div className='c'>
              <h2>我是儿子</h2>
              {value.car}
          </div>
      )
     }
    

八、useReducer (类似 redux 的功能 api )

  • 语法:
    const [state, dispatch] = useReducer(reducer,intialState)
    reducer //函数, intialState //对象
    适合做复杂父子通信

九、 render props

  • 向组件内部动态传入带内容的结构(标签)
    • Vue中:
      • 使用slot技术, 也就是通过组件标签体传入结构
      • React中:
        • 使用children props: 通过组件标签体传入结构
        • 使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性

十、 Fragment (文档碎片)

  • 语法
    <Fragment><Fragment>
    <></>
    
  • 作用
    1. 如果在html标签包裹错误时,渲染的静态html会出现问题,借助Fragment,可以解决这个问题, 具体可看 https://cloud.tencent.com/developer/article/1923532
    2. 可以不用必须有一个真实的DOM根标签,可以用Fragment或者<>代替。
  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值