React基础知识整理学习六

useEffect发送请求

格式:

// 正确使用
useEffect(() => {
  // 定义一个函数
  async function fetchMyAPI() {
    let url = 'http://something/' + productId
    const response = await myFetch(url)
  }
  // 调用它
  fetchMyAPI()
}, [productId])
复制代码

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

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

格式:

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的4种情况

情况1:不带第二个参数。执行时机:初始执行,每次更新之后都要执行。

模拟 componentDidmount + componentDidUpdate

useEffect(() => {
  // 副作用函数的内容
})
复制代码

情况2:带第二个参数,参数是空数组。执行时机:只执行第一次。

模拟 componentDidMount

useEffect(() => {
  // 副作用函数的内容
}, [])
复制代码

使用场景:1 事件绑定 2 发送请求获取数据 等。

情况3:带第二个参数(数组格式),并指定了依赖项。执行时机:(1)初始执行一次 (2)依赖项的值变化了,执行一次

useEffect(() => {
  // 副作用函数的内容
}, [依赖项1,依赖项2,....])
复制代码

这里的依赖项就是组件中定义的状态。

情况4:依赖项为空,没有具体的副作用函数,有副作用函数的清理函数

模拟:componentWillUnMount

useEffect(() => {
  
  return () => {
    // 副作用函数的清理函数,模拟 componentWillUnMount
  }
  
}, [])
复制代码

示例:

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

function Com1 () {
  useEffect(() => {
    const fn = (e) => {
      console.log(e.pageX, e.pageY)
    }
+    window.addEventListener('mousemove', fn)

+    return () => {
      window.removeEventListener('mousemove', fn)
      console.log('组件删除了')
    }
  }, [])
  return <div>子组件</div>
}
export default function App () {
  const [isShow, setIsShow] = useState(true)
  return (
    <div>
      {isShow && <Com1 />}
      <button onClick={() => setIsShow(!isShow)}>切换</button>
    </div>
  )
}
ReactDom.render(<App />, document.getElementById('root'))
复制代码

自定义hooks

除了使用内置的 Hooks 之外,还可以创建自己的 Hooks(自定义 Hooks)。

自定义 Hooks 针对不同组件实现不同状态逻辑复用

  • 自定义 Hooks 是一个函数,约定函数名称必须以 use 开头,React 就是通过函数名称是否以 use 开头来判断是不是 Hooks
  • Hooks 只能在函数组件中或其他自定义 Hooks 中使用,否则,会报错!
  • 自定义 Hooks 用来提取组件的状态逻辑,根据不同功能可以有不同的参数和返回值(就像使用普通函数一样)

示例:

import React, { useState } from 'react'
import ReactDOM from 'react-dom'
import useTimer from './CountDownFunc'

export default function CountDown () {
  const [ isDisabled, setIsDisabled ] = useState(false)
  // 调用useTimer逻辑
  const { count, start } = useTimer(10, () => {
    // 倒计时为0后需要执行的函数
    setIsDisabled(false)
  })
  const hClick = () => {
    // 点击按钮后将按钮设置为禁用状态
    setIsDisabled(true)
    // 点击按钮后开启定时器
    start()
  }

  return (
    <div>
      <button disabled={isDisabled} onClick={hClick}>
        {!isDisabled ? '发送短信' : '等待' + count + '秒'}
      </button>
    </div>
  )
}

ReactDOM.render(<CountDown />, document.getElementById('root'))
复制代码
import React, { useEffect } from 'react'
import ReactDOM from 'react-dom'
import useTimer from './CountDownFunc'

export default function ErrorPage () {
  const { count, start } = useTimer(5, () => {
    console.log('https://www.baidu.com/')
  })
  console.log(start)
  useEffect(
    () => {
      start()
    },
    [ start ]
  )
  return (
    <div>
      页面走丢了,{count}秒后跳转或<a href="https://www.baidu.com/">点我</a>跳转
    </div>
  )
}

ReactDOM.render(<ErrorPage />, document.getElementById('root'))
复制代码

封装逻辑:

import { useState, useEffect, useRef, useCallback } from 'react'
export default function useTimer (time = 10, callback = () => {}) {
  const refText = useRef(null)
  const [ count, setCount ] = useState(time)
  // 用于用户自己开启定时器
  const start = useCallback(
    () => {
      setCount(time)
      refText.current = setInterval(() => {
        console.log(1)
        return setCount((count) => {
          return count - 1
        })
      }, 1000)
    },
    [ time ]
  )

  // 用于极端情况下关闭组件后清除定时器
  useEffect(() => {
    return () => {
      clearInterval(refText.current)
    }
  }, [])
  // 当倒计时为0时停止定时器
  useEffect(
    () => {
      if (count === 0) {
        // 当倒计时清零后用户自己定义需要执行的操作
        callback()
        clearInterval(refText.current)
      }
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [ count ]
  )
  return {
    count,
    start
  }
}
复制代码

useRef-操作DOM

  • 参数:在获取 DOM 时,一般都设置为 null
  • 返回值:包含 current 属性的对象。
  • 注意:只要在 React 中进行 DOM 操作,都可以通过 useRef Hook 来获取 DOM(比如,获取 DOM 的宽高等)。
  • 注意:useRef不仅仅可以用于操作DOM,还可以操作组件
// 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'))
复制代码

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

使用步骤:

// 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'))
复制代码

错误示例:

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'))
复制代码

数据更新会重新执行函数组件,执行timeId = null,所以无法清除定时器.

useContext-全局状态

useContext使用步骤

(1)在context.js文件中导入并调用createContext方法,导出

```
 import { createContext } from 'react'
 const { Provider, Consumer } = createContext()
 export { Provider, Consumer }
```
复制代码

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

```
import { Provider } from './context.js'
return (
  <Provider value={{ hChange, num }}>
    <根组件的内容/>
  </Provider>
)
```
复制代码

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

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

```
import { Consumer } from './context.js'
const 函数组件 = () => {

    return (   <Consumer>
    {(data) => {
      return (
        <div>
         num:{data.num}
          <button onClick={data.hChange}>+1</button>
        </div>
      )
    }}
  </Consumer> )
}
```
复制代码

おすすめ

転載: juejin.im/post/7034089829402935332