能够在函数组件中通过useEffect发送ajax请求
// 正确使用
useEffect(() => {
// 定义一个函数
async function fetchMyAPI() {
let url = 'http://something/' + productId
let config = {}
const response = await myFetch(url)
}
// 调用它
fetchMyAPI()
}, [productId])
注意:effect 只能是一个同步函数,不能使用 async
// 错误演示:
// 不要给 effect 添加 async
useEffect(async () => {
const res = awiat xxx()
return ()=> {
}
}, [])
掌握副作用函数的返回值的使用,能在在组件卸载的时候,清除注册的事件
格式:
useEffect(() => {
// 副作用函数的内容
return 副作用函数的返回值
}, [])
副作用函数的返回值是可选的,可省略。也可以返回一个清理函数,用来清理副作用
useEffect(() => {
// 副作用函数的内容
return ()=>{ /* 做清理工作*/ } // 清理函数
}, [])
清理函数的执行时机:
import React, { useEffect, useState } from 'react'
import ReactDom from 'react-dom'
function Com1 () {
useEffect(() => {
console.log('子组件的副作用函数')
return ()=>{
console.log('子组件的副作用函数的清理函数')
}
}, [])
return 子组件
}
export default function App () {
const [isShow, setIsShow] = useState(true)
const [count, setCcount] = useState(0)
useEffect(() => {
console.log('父组件的副作用函数的内容')
return () => {
console.log('父组件的副作用函数的 清理函数 的内容')
}
}, [count])
return (
{isShow && }
)
}
ReactDom.render( , document.getElementById('root'))
useEffect(()=>{
return ()=>{
// 做清理工作
}
}, [])
情况1:不带第二个参数。执行时机:初始执行,每次更新之后都要执行。
模拟 componentDidmount + componentDidUpdate
useEffect(() => {
// 副作用函数的内容
})
情况2:带第二个参数,参数是空数组。执行时机:只执行第一次。
模拟 componentDidMount
useEffect(() => {
// 副作用函数的内容
}, [])
使用场景:1 事件绑定 2 发送请求获取数据 等。
情况3:带第二个参数(数组格式),并指定了依赖项。执行时机:(1)初始执行一次 (2)依赖项的值变化了,执行一次
useEffect(() => {
// 副作用函数的内容
}, [依赖项1,依赖项2,....])
这里的依赖项就是组件中定义的状态。
情况4:依赖项为空,没有具体的副作用函数,有副作用函数的清理函数
模拟:componentWillUnMount
useEffect(() => {
return () => {
// 副作用函数的清理函数,模拟 componentWillUnMount
}
}, [])
能够理解逻辑复用的含义
目前还有另一功能,要跟踪鼠标的位置,并在页面上输出。
仿写代码如下:
import { useEffect, useState } from 'react'
import ReactDom from 'react-dom'
export default function App () {
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 (
{position.x}, {position.y}
)
}
ReactDom.render( , document.getElementById('root'))
除了使用内置的 Hooks 之外,还可以创建自己的 Hooks(自定义 Hooks)。
自定义 Hooks 针对不同组件实现不同状态逻辑复用。
将组件状态逻辑提取到可重用的函数(自定义 Hooks)中,实现状态逻辑复用。
// museMouse.js
import { useEffect, useState } from 'react'
export default function useMouse() {
// 逻辑处理
const [position, setPosition] = useState({
x: 0,
y: 0,
})
useEffect(() => {
const move = (e) => {
setPosition({
x: e.pageX,
y: e.pageY,
})
}
document.addEventListener('mousemove', move)
return () => {
document.removeEventListener('mousemove', move)
}
}, [])
// 返回状态
return position
}
能够使用useRef操作DOM
使用场景:在 React 中进行 DOM 操作时,用来获取 DOM
作用:返回一个带有 current 属性的可变对象,通过该对象就可以进行 DOM 操作了。
const inputRef = useRef(null)
解释:
参数:在获取 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 com1, {this.state.a}
}
}
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 (
useRef, {' '}
)
}
ReactDom.render( , document.getElementById('root'))
函数组件虽然非常直观,简化了思考 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 (
count:{count}
)
}
ReactDom.render( , document.getElementById('root'))
setCount会导致组件重新渲染,而重新渲染时,会重复执行如下代码
let timeId = null
所以,无法保存timeId
将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 (
count:{count}
)
}
ReactDom.render( , document.getElementById('root'))
写代码完成效果
描述:点击按钮:按钮不可用,开始倒计时10秒,10秒结束之后,按钮恢复可用
import React, { useState, useEffect, useRef } from 'react'
import ReactDom from 'react-dom'
export default function App () {
// 多次渲染之间共享数据:定时器
const timeId = useRef(null)
const [canSend, setCanSend] = useState(true)
const [count, setCount] = useState(initCount)
const send = () => {
setCanSend(false)
setCount(10)
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)
// 倒计时结束
// 恢复按钮可用
setCanSend(true)
}
},
[count]
)
return (
)
}
ReactDom.render( , document.getElementById('root'))
/* 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 (
你要的页面不存在,{count}秒之后,自动跳到主页。或者点击这里跳转
)
}
ReactDom.render( , document.getElementById('root'))
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 (
)
}
ReactDom.render( , document.getElementById('root'))
掌握useContext的使用
目录
- index.js # 根组件,包含father - father.js # 父组件,包含son - son.js # 子组件
index.js
import React, { useState, useContext } from 'react'
import Father from './father'
export default function App() {
const [color, setColor] = useState('red')
return (
根组件:color:{color}
)
}
father.js
import Son from './son.js'
const Father = () => {
return (
父组件
)
}
export default Father
son.js
const Son = () => {
return (
子组件: color: {'red'})
}
export default Son
共3步:
导入并调用createContext方法,得到Context对象,导出
import { createContext } from 'react'
export const Context = createContext()
2.使用 Provider 组件包裹根组件,并通过 value 属性提供要共享的数据
return (
<根组件的内容/>
)
3.在任意后代组件中,如果希望获取公共数据:
导入useContext;调用useContext(第一步中导出的context) 得到value的值
import React, { useContext } from 'react'
import { Context } from './index'
const 函数组件 = () => {
const 公共数据 = useContext(Context)
return ( 函数组件的内容 )
}
index.js
import React, { useState, useContext } from 'react'
import Father from './father'
export const Context = React.createContext() // 这里有导出
export default function App() {
const [color, setColor] = useState('red')
return (
根组件:color:{color}
)
}
father.js
import Son from './son.js'
const Father = () => {
return (
父组件
)
}
export default Father
son.js
import React, { useContext } from 'react'
import { Context } from './index'
const Son = () => {
const color = useContext(Context)
return (
子组件: color: {color})
}
export default Son