目录
一、useEffect副作用
副作用: 平常把一些 除了定义 和 dom 一些无关的代码放到effect里,(例如请求ajax)
(1)使用useEffect
import { useEffect } from 'react'
useEffect(() => {
console.log('useEffect1')
})
(2)执行时机(第二个参数)
情况一:不写第二个参数,页面创建 和 数据更新会执行
useEffect(() => {
console.log('useEffect1')
})
情况二:第二个参数是空数组 执行时机: 只有在组件创建时会执行一次
useEffect(() => {
console.log('useEffect2')
}, [])
情况三:第二个参数 依赖项 执行时机:页面创建 和依赖数据更新了 会执行 注意写法是 [ 变量 ]
useEffect(() => {
console.log('useEffect3')
}, [a])
(3)倒计时demo
import React, { useState, useEffect } from 'react'
import ReactDOM from 'react-dom/client'
const Ren = function () {
const [a, setA] = useState(100)
useEffect(() => {
setInterval(() => {
console.log('倒计时', a)
setA(e => e - 1)
}, 1000)
}, [])
return <div>倒计时100个数 {a} </div >
}
(4)请求ajax
import React, { useEffect, useState } from 'react'
import ReactDOM from 'react-dom/client'
import axios from 'axios'
const Ren = function () {
useEffect(() => {
axios({
method: 'GET',
url: 'https://www.xxx.cn/api/cart',
data: {}
}).then(res => {
console.log(res)
})
})
return <div></div>
}
写成 async await形式
错误写法:
useEffect(async () => {
await axios({
method: 'GET',
url: 'https://www.xxx.cn/api/cart',
data: {}
}).then(res => {
console.log(res)
})
})
正确写法:
useEffect(() => {
const getList = async () => {
const res = await axios({
method: 'GET',
url: 'https://www.xxx.cn/api/cart',
data: {}
})
console.log(res)
}
getList()
})
async 后面要跟一个promise 而useEffect并不是 所以 我们可以在useEffect里定义一个函数 直接调用
(5)useEffect的清理函数
副作用函数的返回值是可选的,也可以省略,也可以返回一个清理函数 清理副作用
执行时机:清理函数会在 组件卸载时以及下次副作用函数调用之前执行
useEffect(() => {
return () => // 做清理工作
})
demo 清除鼠标移动事件
import React, { useEffect, useState } from 'react'
// import ReactDOM from 'react-dom/client'
import ReactDOM from 'react-dom'
const Ren = function () {
useEffect(() => {
const f = (e) => {
console.log(e.screenX, e.screenY)
}
window.addEventListener('mousemove', f)
return () => {
window.removeEventListener('mousemove', f)
}
})
return <h1>111</h1>
}
export default function Use () {
const [isShow, toggle] = useState(true)
const hide = () => {
toggle(false)
}
return (
<div>
{isShow && <Ren></Ren>}
<div onClick={hide}>切换</div>
</div>
)
}
// const root = ReactDOM.createRoot(document.getElementById('root'))
// root.render(<Use />)
ReactDOM.render(<Use></Use>, document.getElementById('root'))
在上面代码中 在组件里绑定了鼠标事件 当销毁组件时 事件并不会被销毁 这时候可以利用useEffect清理函数来清除掉 鼠标移动事件
(6)图片移动demo
import React, { useEffect, useState } from 'react'
// import ReactDOM from 'react-dom/client'
import ReactDOM from 'react-dom'
import img from '../images/avatar.png'
const Ren = function () {
const [X, setX] = useState(0)
const [Y, setY] = useState(0)
useEffect(() => {
const f = (e) => {
console.log(e.screenX, e.screenY)
setX(() => e.screenX)
setY(() => e.screenY)
}
window.addEventListener('mousemove', f)
return () => {
window.removeEventListener('mousemove', f)
}
})
return (
<div style={{ width: "50px", height: "50px" }}>
<img src={img} style={{ width: "50px", height: "50px", position: "absolute", left: X + 'px', top: Y + 'px' }}></img>
</div>
)
}
// const root = ReactDOM.createRoot(document.getElementById('root'))
// root.render(<Use />)
ReactDOM.render(<Ren></Ren>, document.getElementById('root'))
(7)图片移动demo 封装
mouseMoveXY.js
import React, { useEffect, useState } from 'react'
// 如果是函数组件首字母要大写 如果是hooks函数 要以use开头
// 调用要在 函数组件里面调用
export default function useMouseMove () {
const [X, setX] = useState(0)
const [Y, setY] = useState(0)
useEffect(() => {
const f = (e) => {
console.log(e.screenX, e.screenY)
setX(() => e.screenX)
setY(() => e.screenY)
}
window.addEventListener('mousemove', f)
return () => {
window.removeEventListener('mousemove', f)
}
})
return { X, Y }
}
使用
import React, { useEffect, useState } from 'react'
// import ReactDOM from 'react-dom/client'
import ReactDOM from 'react-dom'
import img from '../images/avatar.png'
import mousemove from './mouseMoveXY'
const Ren = function () {
const { X, Y } = mousemove()
return (
<div style={{ width: "50px", height: "50px" }}>
<img src={img} style={{ width: "50px", height: "50px", position: "absolute", left: X + 'px', top: Y + 'px' }}></img>
</div>
)
}
ReactDOM.render(<Ren></Ren>, document.getElementById('root'))
二、 useRef(引用)
(1)使用方法
1.导入 import React, { useRef } from 'react'
2.定义 const getInput = useRef(null)
3.使用 ref={getInput}
(2)demo 获取input的值
import React, { useRef, useState } from 'react'
// import ReactDOM from 'react-dom/client'
import ReactDOM from 'react-dom'
const Ren = function () {
const getInput = useRef(null)
console.log(getInput)
const [text, SetText] = useState('')
const edit = (e) => {
console.log(e.target.value)
SetText(() => e.target.value)
}
const getText = () => {
console.log(getInput.current.value)
}
return (
<div>
<input value={text} onChange={edit} ref={getInput}></input>
<button onClick={getText}>获取input的值</button>
</div>
)
}
ReactDOM.render(<Ren></Ren>, document.getElementById('root'))
(3)demo 清除定时器
import React, { useEffect, useRef, useState } from 'react'
// import ReactDOM from 'react-dom/client'
import ReactDOM from 'react-dom'
const Ren = function () {
const getInput = useRef(null)
console.log(getInput)
const timer = useRef(null)
const [Num, setNum] = useState(0)
// let timer = null //相当于每次都是在赋值一个null 错误写法
useEffect(() => {
// timer = setInterval(() => {
timer.current = setInterval(() => {
setNum(() => Num + 1)
}, 1000)
}, [])
const edit = (e) => {
}
const clearTimer = () => {
// clearInterval(timer)
clearInterval(timer.current)
}
return (
<div>
<div>倒计时 {Num} </div>
<button onClick={clearTimer}>清除定时器</button>
</div>
)
}
ReactDOM.render(<Ren></Ren>, document.getElementById('root'))
如果在外层定义一个timer 定义定时器 当每次倒计时 值发生改变时 整个组件都会重新执行 相当于每次倒计时都在定义了一个timer 如果想清除定时器 可以 用useRef 把定义的变量 使用 变量.current 来当成定时器 的变量 可以理解为 useRef 定义的 current里永远都是引用类型 不会改变
(4)demo 验证码倒计时
import React, { useEffect, useRef, useState } from 'react'
// import ReactDOM from 'react-dom/client'
import ReactDOM from 'react-dom'
const Ren = function () {
const timer = useRef(null)
const [Num, setNum] = useState(3)
const [Status, setStatus] = useState(false)
const countDown = () => {
timer.current = setInterval(() => {
setNum((Num) => {
setStatus(true)
return Num - 1
})
}, 1000)
}
useEffect(() => {
if (Num == 0) {
clearInterval(timer.current)
setStatus(false)
setNum(3)
}
console.log(Num)
}, [Num])
return (
<div>
<div>倒计时 {Num} </div>
<button disabled={Status} onClick={countDown}>{Status ? Num + '秒后重新获取' : '获取验证码'}</button>
</div>
)
}
ReactDOM.render(<Ren></Ren>, document.getElementById('root'))
(5)demo 验证码倒计时 抽离hooks
import React, { useEffect, useRef, useState } from 'react'
// import ReactDOM from 'react-dom/client'
import ReactDOM from 'react-dom'
const useCount = (count = 10, callback = () => { }) => {
const timer = useRef(null)
const [Num, setNum] = useState(null)
const start = () => {
setNum(count)
timer.current = setInterval(() => {
setNum(num => num - 1)
}, 1000)
}
useEffect(() => {
if (Num == 0) {
clearInterval(timer.current)
callback()
}
}, [Num])
return { start, Num }
}
const Ren = function () {
const [Status, setStatus] = useState(false)
const { start, Num } = useCount(3, () => {
setStatus(false)
})
const countDown = () => {
setStatus(true)
start()
}
return (
<div>
<div>倒计时 {Num} </div>
<button disabled={Status} onClick={countDown}>获取验证码</button>
</div>
)
}
ReactDOM.render(<Ren></Ren>, document.getElementById('root'))
count 作为倒计时秒数、callback作为 倒计时之后的回调函数
demo 验证码倒计时封装抽离出一个组件 useCountDown.js
import { useEffect, useRef, useState } from 'react'
export default function useCountDown (count = 10, callback = () => { }) {
const timer = useRef(null)
const [Num, setNum] = useState(null)
const start = () => {
setNum(count)
timer.current = setInterval(() => {
//组件清楚后仍会执行
console.log(1)
setNum(num => num - 1)
}, 1000)
}
useEffect(() => {
if (Num == 0) {
setNum(count)
clearInterval(timer.current)
callback()
}
}, [Num])
useEffect(() => {
return () => {
clearInterval(timer.current)
}
}, [])
return { start, Num }
}
记得通过useEffect的清理函数 清理一下 定时器事件 极端情况下 销毁组件后 延时器仍会执行
使用
import React, { useState } from 'react'
import ReactDOM from 'react-dom'
import useCountDown from './useCountDown'
const Ren = function () {
const [Status, setStatus] = useState(false)
const { start, Num } = useCountDown(3, () => {
setStatus(false)
})
const countDown = () => {
setStatus(true)
start()
}
return (
<div>
<div>倒计时 {Num} </div>
<button disabled={Status} onClick={countDown}>获取验证码</button>
</div>
)
}
ReactDOM.render(<Ren></Ren>, document.getElementById('root'))
三、useContext(跨组件传值)
一、使用方法
1.导如并调用createContext 得到 Context对象 导出
import React, { useState, createContext } from 'react'
export const Context = createContext()
2.使用Provider包裹根组件 通过value属性提供共享数据
return (
<Context.Provider value={{ a: 1, b: 2 }}>
<根组件内容/>
</Context.Provider>
)
3. 子代组件接收
import { useContext } from 'react'
const value = useContext(Context)
console.log(value)
二、传值Demo
index.js 根组件
import React, { createContext } from 'react'
import ReactDOM from 'react-dom'
import Father from './father'
export const Context = createContext()
const Ren = function () {
return (
<Context.Provider value={{ a: 1, b: 2 }}>
<div>
<div>根组件</div>
<div style={{ border: '1px solid #ccc' }}> <Father /></div>
</div>
</Context.Provider>
)
}
ReactDOM.render(<Ren></Ren>, document.getElementById('root'))
father.js 父组件
import Son from './son.js'
function Father () {
return (
<div>
<div>父组件</div>
<div style={{ border: '1px solid #ccc' }}> <Son /></div>
</div>
)
}
export default Father
son.js 子组件
import { Context } from './index'
import { useContext } from 'react'
function Son1 () {
const value = useContext(Context)
return (
<div>
<div> 孙子组件: {value.a} {value.b} </div>
</div>
)
}
export default Son1