目录
useState
const [初始值,修改值的方法] = useState( 初始值 )
我们用useState定义一个初始值,可以打印看一下结果
console.log(useState(10)) // [10, ƒ]
结果是一个数组,第0项就是我们定义的初始值,第1项则就是我们修改值的方法
我们用数组接收,就是结构出来我们需要的这两项
import React, { useState } from 'react'
import { Button } from 'antd'
export default function Home() {
console.log(useState(10))
// 定义简单数据类型
const [num, setNum] = useState(10)
// 定义复杂数据类型
const [obj, setObj] = useState({ age: 25 })
// 如果初始化需要进行计算得出,我们也可以传入一个函数
const [count, setCount] = useState(() => {
let num = 100
// ...
return num
})
const addNum = () => {
// 修改简单数据类型,可以这样直接修改
setNum(num + 1)
}
const insertName = () => {
// 修改复杂数据类型,1. 可以利用扩展运算符 2. 也可以深拷贝一份,再进行修改
setObj({ ...obj, name: '张三' })
}
const changeCount = () => {
setCount(999)
}
return (
<div>
<h2 style={
{ textAlign: 'center' }}>Home</h2>
<p>num的值: {num}</p>
<Button onClick={addNum}>num+1</Button>
<p>
obj的值: {obj.age} - {obj.name}
</p>
<Button onClick={insertName}>obj新增name属性</Button>
<p>count的值: {count}</p>
<Button onClick={changeCount}>修改count的值</Button>
</div>
)
}
useState()还有一个特性,就是如果更新的数据与上次一样,React将跳过子组件的渲染及effect的执行
代码验证
父组件
import React, { useEffect, useState } from 'react'
import MusicSon from './MusicSon'
import { Button } from 'antd'
export default function Music() {
const [num, setNum] = useState(100)
const numAdd = () => {
// 我们看一下值没有改变的情况下,会不会触发effect和子组件
setNum(100)
}
useEffect(() => {
console.log('初始化effect执行了')
}, [])
return (
<div>
<h2>Music</h2>
<Button onClick={numAdd}>num不变</Button>
<MusicSon toSon={num} />
</div>
)
}
子组件
import React from 'react'
export default function MusicSon(props) {
console.log('子组件触发执行啦')
return (
<div>
<h5>MusicSon</h5>
<p>从父组件接收过来的数据:{props.toSon}</p>
</div>
)
}
发现只有一进来的时候触发了effect和子组件
然后我们点击按钮,这时值没有发生改变,发现控制台什么都没有输出,说明只要更新的数据与上次一样,React将跳过子组件的渲染及effect的执行
useEffect
useEffect
都是在组件dom渲染更新完毕之后才执行的
利用其副作用,执行一些操作,那什么是副作用呢?
上面是文档给出的副作用,看着挺懵逼的,其实说简单点,函数的副作用就是函数除了返回值外对外界环境造成的其它影响 。不理解也没事,其实我也不太理解(😜)
常见的副作用
- 数据请求 ajax发送
- 手动修改dom
- localstorage操作
下面说用法:
1.不添加依赖项
- 组件首次渲染执行一次,以及不管是哪个状态更改引起组件更新时都会重新执行
- 组件初始渲染
- 组件更新 (不管是哪个状态引起的更新)
useEffect(()=>{
console.log('副作用执行了')
})
2. 添加空数组
- 组件只在首次渲染时执行一次
useEffect(()=>{
console.log('副作用执行了')
},[])
3. 添加特定依赖项
- 副作用函数在首次渲染时执行,在依赖项发生变化时重新执行
- 组件初始渲染
- 依赖项发生变化时
function App() {
const [count, setCount] = useState(0)
const [name, setName] = useState('zs')
useEffect(() => {
console.log('副作用执行了')
}, [count])
return (
<>
<button onClick={() => { setCount(count + 1) }}>{count}</button>
<button onClick={() => { setName('cp') }}>{name}</button>
</>
)
}
注意事项
- useEffect 回调函数中用到的数据(比如,count)就是依赖数据,就应该出现在依赖项数组中,如果不添加依赖项就会有bug出现
清除副作用
- 在组件被销毁时,如果有些副作用操作需要被清除(比如定时器)
语法:
useEffect(() => {
// 副作用操作…
return () => {
// 写清除副作用的代码
}
})
eg: 清除定时器案例
父组件
import React from 'react'
import { useState } from 'react'
import Son from './pages/Son'
export default function App() {
const [flag, setFlag] = useState(true)
return (
<div>
<h2>App</h2>
<button onClick={() => setFlag(!flag)}>显示/隐藏组件</button>
{flag && <Son></Son>}
</div>
)
}
子组件
import React, { useEffect } from 'react'
export default function Son() {
// 组件进来的时候触发一个定时器
useEffect(() => {
let timer = setInterval(() => {
console.log('定时器执行了')
}, 1000)
// 组件销毁时清除定时器
return () => {
// 在return里面的函数里写清除操作
clearInterval(timer)
}
}, [])
return (
<div>
<h3>Son</h3>
</div>
)
}
useEffect
发送网络请求
- 依赖项要是一个空数组,因为依赖项为空数组时只会在页面初始化时触发一次
import React from 'react'
import { useEffect } from 'react'
export default function App() {
const getData = () => {
fetch('https://cnodejs.org/api/v1/topics')
.then((response) => response.json())
.then((data) => console.log(data.data))
}
useEffect(() => {
getData()
}, [])
return (
<div>
<h2>App</h2>
</div>
)
}
useRef
- 获取元素的真实Dom
const 变量名 = useRef(null)
我们用useRef()定义一下,然后打印一下看看
console.log(useRef(null))
可以看到,里面是有一个current属性,而这个属性里面存放的就是dom元素的内容,我们在useRef()中定义什么,current里就显示什么。需要获取那个元素的DOM,则直接让此元素的ref等于我们useRef()方法接收的变量即可
案例
在输入框中输入,我们点击按钮,获取到输入的内容
import React, { useRef } from 'react'
import { Button, Input } from 'antd'
export default function Home() {
const ipt = useRef(null)
const getValue = () => {
console.log(ipt)
}
return (
<div>
<h2>Home</h2>
<Input placeholder="请输入内容" ref={ipt} />
<Button onClick={getValue}>提交</Button>
</div>
)
}
我们可以看到,current里面存放了一些属性和方法,其中的input里的value才是我们需要的
import React, { useRef } from 'react'
import { Button, Input } from 'antd'
export default function Home() {
const ipt = useRef(null)
const getValue = () => {
console.log('输入的值为', ipt.current.input.value)
}
return (
<div>
<h2>Home</h2>
<Input placeholder="请输入内容" ref={ipt} />
<Button onClick={getValue}>提交</Button>
</div>
)
}
useContext
一般深层组件传值用,类似于Vue的provide和inject
- 传输的数据是响应式的,跨组件传输数据用
- 如果想要传递的数据,只在整个应用初始化的时候传递一次就可以,则可以在
index.js
文件中提供数据(静态) - 如果传递的数据需要状态维护,则可以在
app.js
中提供数据(动态)
使用步骤
- 创建一个
context
的文件 - 使用
createContext
创建Context
对象,并导出 - 在顶层组件引入,通过
Provider
提供数据 - 在底层组件引入,通过
useContext
函数获取数据
context.js
import { createContext } from 'react'
const Context = createContext()
export default Context
上层组件
import React, { useState } from 'react'
import Son from './pages/Son'
// 1. 引入Context
import Context from './utils/context.js'
export default function App() {
const [msg] = useState('根组件传递的数据')
return (
<>
{/* 2. 使用Provider包裹上层组件提供数据 */}
<Context.Provider value={msg}>
{/* 根组件 */}
<div>
<h2>App</h2>
<Son></Son>
</div>
</Context.Provider>
</>
)
}
下层组件
import React, { useContext } from 'react'
import Context from '../utils/context.js'
export default function Son() {
let val = useContext(Context)
return (
<div>
<h3>Son</h3>
<p>从根组件得到的数据 --- {val}</p>
</div>
)
}
useCallback
是一个带有记忆的函数,一般用作优化用
基本写法
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
接收两个参数
- 第一个参数是处理函数
- 第二个参数是一个数组,用于指定被记忆的函数更新所依赖的值
只有第二个参数数组中的值发生改变了,第一个参数的处理函数才会执行
useCallback(fn, deps)
相当于useMemo(() => fn, deps)
代码验证
- 只有第二个参数中数组中的num变化时,才会触发
useCallback中的第一个函数。如果第二个参数写一个空数组,则只会执行一次
import React, { useCallback, useState } from 'react'
export default function Home() {
const [num, setNum] = useState(10)
const changeNum = useCallback(() => {
setNum(num + 1)
}, [num])
return (
<div>
<h2>{num}</h2>
<button onClick={changeNum}>改变num的值</button>
</div>
)
}
为什么要使用useCallback() ?
函数式组件中,定义在组件内的函数会随着状态值的更新而重新渲染,因此函数中定义的函数会被频繁定义,在父子组件的通信中这样是非常消耗性能的。使用useCallback()结合memo就可以有效的减少子组件更新频率,提高效率和性能。
代码验证
父组件
import React, { useCallback, useState } from 'react'
import HomeSon from './HomeSon'
export default function Home() {
const [num, setNum] = useState(10)
const [toSon, setToSon] = useState(999)
const changeNum = useCallback(() => {
setNum(num + 1)
}, [num])
const changeToSon = useCallback(() => {
setToSon(toSon + 1)
}, [toSon])
return (
<div>
<h2>{num}</h2>
<button onClick={changeNum}>改变num的值</button>
<HomeSon toSon={toSon} />
<button onClick={changeToSon}>改变传给子组件的值</button>
</div>
)
}
子组件
- 用React.memo()包裹的组件,会监听props,只有props改变了,才会触发此组件的更新
import React from 'react'
export default React.memo(function HomeSon(props) {
const { toSon } = props
console.log('子组件触发了')
return (
<div>
<h3>HomeSon --- 从父组件接收过来的: {toSon}</h3>
</div>
)
})
这样的话,只有1. 初始化的时候以及 2. 子组件接收的数据发生变化时,才会触发子组件的渲染
useMemo
基本写法(作用类似于Vue的计算属性,只要依赖项不发生改变,就不会重新计算)
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
接收两个参数
- 第一个参数是“创建”函数,用于处理耗时计算并返回需要记录的值
- 第二个参数是依赖项数组,用于指定第一个参数“创建”函数更新所依赖的值
仅会在某个依赖项改变时才重新计算。传入useMemo的函数会在渲染期间执行。但是不要在这个函数内部执行与渲染无关的操作,例如副作用,副作用这类的操作属于useEffect的使用范畴。
如果没有提供依赖项数组,useMemo在每次渲染时都会计算新的值
类似于Vue的计算属性,也会进行缓存。计算结果没有改变时,函数也不会触发
代码验证
需求:c的值为a+b,我们改变a或者b,c都能计算出最新的结果
import React, { useMemo, useState } from 'react'
export default function Movie() {
const [a, setA] = useState(0)
const [b, setB] = useState(0)
const aAdd = () => {
setA(a + 1)
}
const bAdd = () => {
setB(b + 1)
}
// 类似于Vue的计算属性
const c = useMemo(() => {
return a + b
}, [a, b])
return (
<div>
<p>{a}</p>
<button onClick={aAdd}>a+</button>
<p>{b}</p>
<button onClick={bAdd}>b+</button>
<p>a + b = {c}</p>
</div>
)
}
useMemo()的第一个函数还可以返回一段DOM
import React, { useMemo, useState } from 'react'
export default function Movie() {
const [a, setA] = useState(0)
const [b, setB] = useState(0)
const aAdd = () => {
setA(a + 1)
}
const bAdd = () => {
setB(b + 1)
}
// 类似于Vue的计算属性
const c = useMemo(() => {
return <h2>a + b 计算的结果是: {a + b}</h2>
}, [a, b])
return (
<div>
<p>{a}</p>
<button onClick={aAdd}>a+</button>
<p>{b}</p>
<button onClick={bAdd}>b+</button>
<p>{c}</p>
</div>
)
}
useMemo()与useCallBack()的区别?
- useMemo传入的函数内部需要有返回值
- useMemo只能声明在函数式组件内部(不像React.memo(),可以用在组件外部)
useImperativeHandle
作用
- 父组件可以获取子组件的DOM元素
- 父组件可以获取子组件的方法
- 父组件可以获取子组件的属性(又叫状态,或者变量,一个意思)
使用useImperativeHandle这个hook之前,首先我们需要了解forwardRef这个方法
forwardRef()这个方法是React提供的,React.forwardRef() 或者 从React结构出来进行使用
import React, { forwardRef } from 'react'
export default forwardRef(function 组件名(props, ref) { ... }
或者
import React from 'react'
export default React.forwardRef(function MoneySon(props, ref) { ... }
用forwardRef()包裹的函数式组件,可以替换函数的第二个参数。正常函数式组件第一个参数是props,第二个参数是上下文对象context。但是用forwardRef()包裹的,第二个参数就是ref(父组件中子组件标签的ref值),第一个参数还是props。
父组件
import React, { useRef } from 'react'
import MoneySon from './MoneySon'
export default function Money() {
const pRef = useRef(null)
const getSonP = () => {
console.log(pRef.current)
}
return (
<div>
<h2>Money</h2>
<button onClick={getSonP}>获取子组件的一个元素</button>
<MoneySon ref={pRef} />
</div>
)
}
子组件
import React, { forwardRef } from 'react'
export default forwardRef(function MoneySon(props, ref) {
return (
<div>
<h5>MoneySon</h5>
<p ref={ref}>子组件的p</p>
{/* <h2 ref={ele1}>1</h2>
<h3 ref={ele2}>2</h3>
<Button ref={btn} onClick={clickBtn}>
点击
</Button> */}
</div>
)
})
结果如图
了解了这个之后,我们再引入 useImperativeHandle() ,这个方法有3个参数
- 从父组件传过来的子组件标签上ref值
- 回调函数,返回一个对象,对象中我们可以写子组件中的变量,方法,和dom元素的ref值
- 依赖项数组,放在里面的依赖项发生改变时,会重新触发回调函数
父组件
import React, { useRef } from 'react'
import MoneySon from './MoneySon'
export default function Money() {
const pRef = useRef(null)
const getElement = () => {
console.log(pRef.current)
// 获取子组件的DOM
console.log(pRef.current.pp.current)
// 获取子组件的方法
// pRef.current.clickBtn()
// 获取子组件的属性(状态/变量)
// console.log(pRef.current.sonData)
}
return (
<div>
<h2>Money</h2>
<button onClick={getElement}>点击获取子组件多个元素</button>
<MoneySon ref={pRef} />
</div>
)
}
子组件
import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react'
import { Button } from 'antd'
export default forwardRef(function MoneySon(props, ref) {
const pp = useRef()
const [sonData, setSonData] = useState(100)
const clickBtn = () => {
console.log('按钮被点击了')
setSonData(sonData + 1)
}
useImperativeHandle(ref, () => {
return { pp, clickBtn, sonData }
})
return (
<div>
<h5>MoneySon</h5>
<Button onClick={clickBtn}>点击</Button>
<p ref={pp}>{sonData}</p>
</div>
)
})
首次点击如图
先点击几次,新增数据,然后再点击获取
原文链接:https://blog.csdn.net/qq_52845451/article/details/129792701
此处评论已关闭