从零开始React(8)==>hooks进阶

一.如何用useEffect发送请求?

在hooks基础里面我们了解到useEffect里面可以进行:数据(Ajax)请求、手动修改 DOM、开启定时器,清空定时器,添加事件监听,删除事件, localStorage 操作等,那么我应该如何在useEffect里面进行发送请求呢?

示列代码:

import React, { useEffect } from 'react'
import ReactDom from 'react-dom'
import axios from 'axios'
export default function App () {
  //   const id = 100
  // 组件挂载完成,发ajax
  useEffect(() => {
    const getData = async () => {           // async不能写在useEffect上面
      const res = await axios({
        url: '请求地址',
        method: 'GET'
      })
      console.log(res)
    }

    getData()
  }, [])
  return <div>组件挂载完成,发ajax</div>
}
ReactDom.render(<App />, document.getElementById('root'))

错误示范:

// 错误演示:

// 不要给 effect 添加 async
useEffect(async () => {
    const res = awiat xxx()
    return ()=> {
        
    }
}, [])

effect 只能是一个同步函数,不能使用 async

二.useEffect-副作用函数的返回值-清理副作用

格式:

useEffect(() => {
  // 副作用函数的内容

  return 副作用函数的返回值
}, [])

副作用函数的返回值是可选的,可省略。也可以返回一个清理函数,用来清理副作用

useEffect(() => {
  // 副作用函数的内容

  return ()=>{ /* 做清理工作*/ } // 清理函数
}, [])

清理函数的执行时机:

  • 清理函数在组件卸载时以及下一次副作用函数调用之前执行

示列代码:(执行时机)

import React, { useEffect, useState } from 'react'
import ReactDom from 'react-dom'

function Com1 () {
  useEffect(() => {
    console.log('子组件的副作用函数')
    
    return ()=>{
      console.log('子组件的副作用函数的清理函数')
    }
  }, [])
  return <div>子组件</div>
}
export default function App () {
  const [isShow, setIsShow] = useState(true)
  const [count, setCcount] = useState(0)
  
  useEffect(() => {
    console.log('父组件的副作用函数的内容')

    return () => {
      
      console.log('父组件的副作用函数的 清理函数 的内容')
    }
  }, [count])
  
  return (
    <div>
      {isShow && <Com1 />}
      <button onClick={() => setIsShow(!isShow)}>删除|创建子组件</button>
		<button onClick={() => setCcount(count+1)}>点击+1, {count}</button>
    </div>
  )
}
ReactDom.render(<App />, document.getElementById('root'))

useEffect清理副作用案例(案例-图片跟随鼠标 );

目的:让图片跟随鼠标移动

思路:

  • 通过useState提供状态

  • 通过useEffect给document注册鼠标移动事件

  • 在组件销毁的时候清理副作用

示列代码:

import { useEffect, useState } from 'react'
import img from './1.gif'
export default function Move() {
  const [position, setPosition] = useState({
    x: 0,
    y: 0
  })
  useEffect(() => {
    const move = (e) => {
      console.log('移动')

      setPosition({
        x: e.pageX,
        y: e.pageY
      })
    }
    document.addEventListener('mousemove', move)
    console.log('注册事件')
    return function () {
      document.removeEventListener('mousemove', move)
    }
  }, [])
  return (
    <div>
      <img
        src={img}
        style={{
          position: 'absolute',
          top: position.y + 1,
          left: position.x + 1
        }}
        alt=""
      />
    </div>
  )
}

自定义hook可以看看React--自定义hooks_Meskjei的博客-CSDN博客_自定义hooks作用

useRef-操作DOM

使用场景:在 React 中进行 DOM 操作时,用来获取 DOM

作用:返回一个带有 current 属性的可变对象,通过该对象就可以进行 DOM 操作了。

const inputRef = useRef(null)

代码示列:

// 1. 导入 useRef
import React, { useRef } from 'react'
import ReactDom from 'react-dom'

class Com1 extends React.Component {
  state = {
    a: 100
  }

  render () {
    return <div>com1, {this.state.a}</div>
  }
}

export default function App () {
  // 2. 调用useRef(初值),得到引用对象
  // 3. 把引用对象设置ref 给任意的组件/元素
  // 4. 通过引用对象.current 获取 组件/元素
  const refTxt = useRef(null)
  const refCom1 = useRef(null)
  console.log(refTxt)

  const click = () => {
    console.log(refTxt, refCom1)
    console.log(refCom1.current.state.a)
    // console.log(refTxt.current.value)
    // refTxt.current.style.backgroundColor = 'red'
  }
  return (
    <div>
      useRef, <input ref={refTxt} />{' '}
      <button onClick={click}>点击,获取input中的值</button>
      <br />
      <Com1 ref={refCom1} />
    </div>
  )
}
ReactDom.render(<App />, document.getElementById('root'))

注意: 只要在 React 中进行 DOM 操作,都可以通过 useRef Hook 来获取 DOM(比如,获取 DOM 的宽高等)。

useRef不仅仅可以用于操作DOM,还可以操作组件

函数组件虽然非常直观,简化了思考 UI 实现的逻辑,但是比起 Class 组件,还缺少了一个很重要的能力:在多次渲染之间共享数据。

useRef-在多次渲染之间共享数据(停止定时器案例)

问题: 点击按钮停止定时器

import React, { useEffect, useState } from 'react'
import ReactDom from 'react-dom'
export default function App () {
  const [count, setCount] = useState(0)
  let timeId = null
  console.log(timeId)
  useEffect(() => {
    timeId = setInterval(() => {
      setCount((count) => count + 1)
    }, 1000)
    console.log(timeId)
  }, [])

  const hClick = () => {
    console.log(timeId)
    clearInterval(timeId)
  }

  return (
    <div>
      count:{count}
      <button onClick={hClick}>点击停止定时器</button>
    </div>
  )
}
ReactDom.render(<App />, document.getElementById('root'))

分析:

 setCount会导致组件重新渲染,而重新渲染时,会重复执行如下代码,所以无法保存

let timeId = null

所以需要将timeId保存在一个多次渲染也不会丢失的位置。

示列:

// 1. 导入 useRef
import React, { useRef } from 'react'

组件() {
  // 2. 调用useRef,
  const timeRef = useRef(null)
  // 3. 向引用的current中存入数据
  timeRef.current = 你需要在多次渲染之间共享的数据
}

解决完成代码:

import React, { useEffect, useRef, useState } from 'react'
import ReactDom from 'react-dom'
export default function App () {
  const [count, setCount] = useState(0)
  const timeRef = useRef(null)
  console.log(timeRef.current)
  useEffect(() => {
    timeRef.current = setInterval(() => {
      setCount((count) => count + 1)
    }, 1000)
    console.log(timeRef.current)
  }, [])

  const hClick = () => {
    console.log(timeRef.current)
    clearInterval(timeRef.current)
  }

  return (
    <div>
      count:{count}
      <button onClick={hClick}>点击停止定时器</button>
    </div>
  )
}
ReactDom.render(<App />, document.getElementById('root'))

自定义hook(逻辑复用)

将上面倒计和下面页面倒计中逻辑部分进行封装实现复用

 倒计时案列代码:

/* eslint-disable react-hooks/exhaustive-deps */
import React, { useState, useEffect, useRef } from 'react'
import ReactDom from 'react-dom'
export default function App () {
  const [count, setCount] = useState(5)
  const refTimerId = useRef(null)

  useEffect(() => {
    // 1. 开始倒数
    setCount(5)
    // 2. 启动
    refTimerId.current = setInterval(() => {
      setCount((count) => count - 1)
    }, 1000)
  }, [])

  useEffect(
    () => {
      console.log('观察到count变了', count)
      if (count === 0) {
        //    倒数结束,要:
        // 1. 跳转
        location.href = 'http://www.baidu.com'
        console.log('location.href = http://www.baidu.com')
        // 2.清空定时器
        clearInterval(refTimerId.current)
      }
    },
    [count]
  )

  return (
    <div>
      你要的页面不存在,{count}秒之后,自动跳到主页。或者点击<a href="http://www.baidu.com">这里</a>跳转
    </div>
  )
}

ReactDom.render(<App />, document.getElementById('root'))

提炼hooks:

示列代码

export default function useCountDown (initCount = 10, callBack = () => {}) {
  const timeId = useRef(null)

  const [count, setCount] = useState(initCount)

  const start = () => {
    setCount(initCount)
    timeId.current = setInterval(() => {
      setCount((count) => count - 1)
    }, 1000)
  }
  useEffect(() => {
    return () => {
      console.log('..........')
      clearInterval(timeId.current)
    }
  }, [])

  useEffect(
    () => {
      console.log(count, timeId.current)
      if (count === 0) {
        clearInterval(timeId.current)
        callBack()
      }
    },
    [count]
  )

  return { count, start }
}

完整功能:

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

import ReactDom from 'react-dom'

import useCountDown from './useCountDown' // 引入hooks

export default function App () {
  const { count, start } = useCountDown(10, () => {
    setCanSend(true)
  })
  const [canSend, setCanSend] = useState(true)

  const send = () => {
    setCanSend(false)
    start(60)
  }
  return (
    <div>
      <button disabled={!canSend} onClick={send}>
        {canSend ? '发送验证码' : count + '秒之后再发送'}
      </button>
    </div>
  )
}
ReactDom.render(<App />, document.getElementById('root'))

useContext-全局状态

useContext步骤:

1.导入并调用createContext方法,得到Context对象,导出

import { createContext } from 'react'
export const Context = createContext()

 2.使用 Provider 组件包裹根组件,并通过 value 属性提供要共享的数据

return (
  <Context.Provider value={ 这里放要传递的数据 }>
  	<根组件的内容/>
  </Provider>
)

 3.在任意后代组件中,如果希望获取公共数据:

导入useContext;调用useContext(第一步中导出的context) 得到value的值

import React, { useContext } from 'react'
import { Context } from './index'
const 函数组件 = () => {
    const 公共数据 = useContext(Context)
    return ( 函数组件的内容 )
}

这期代码比较多,没有很多的语言解释,可以用我的代码来看看代码运行结果,关于hooks这里不是很好用语言描述出来.仅供参考.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值