useState
在函数组件中,由于没有像类组件一样有个指向实例组件的 this
,这个this里面储存类组件的状态和修改状态的方式,React为我们提供了 useState
来帮助我们保存组件的状态。
const [state, setState] = useState
(initialState);
用于:用于函数组件定义state,和修改state的方法
返回一个 state,以及更新state的函数。
在初始渲染期间,返回的状态(state)与传入的第一个参数(initialState)值相同。
setState函数用于更新state。他接受一个新的 state 值并将组件的一次重新渲染加入了队列。
import { useState } from 'react'
export default function App() {
// 定义了一个名为name的state,修改这个状态的方法叫做setName,状态的初始值为syukinmei
const [name, setName] = useState('syukinmei')
const [count, setCount] = useState(0)
return (
<>
<button onClick={() => { setName('ebiebi') }}> {name} </button>
{/* 写法1 */}
<button onClick={() => { setCount(count + 1) }}> {count} </button>
{/* 写法2 */}
<button onClick={() => { setCount((prevstate) => prevstate + 1) }}> {count} </button>
</>
)
}
跳过state更新
调用StateHook的更新函数并传入当前的state时,React将跳过子组件的渲染及effect的执行。(React使用 Object.is
比较算法来比较state)
Object.is(value1, value2);
// Object.is()方法确定两个值是否相同
// 返回值:Boolean表示两个参数是否是相同的值
初始值为引用数据类型时
基础数据类型 直接赋值,引用数据类型 解构再赋值给它
原因:React 唯一数据源,状态只读
people状态是不能改的,需要使用setPeople创造一个新状态给他,只是新状态和旧状态名字相同做了一个覆盖。
总结:通过传入 useState 参数后返回一个带有默认状态和改变状态函数的数组。通过传入新状态给函数来改变原本的状态值。值得注意的是:useState不帮我们处理状态,相较于setState非覆盖式更新状态,useState覆盖式更新状态,需要开发者自己处理逻辑
import React from 'react'
export default function App() {
interface People {
name?: string
age?: number
}
const [people, setPeople] = React.useState<People>({ name: 'syukinmei', age: 11 })
const change = () => {
// 方法用于书写修改name的逻辑
// setPeople({
// name:'ebibei'
// })
const a = people
people.name = 'ebiebi'
const b = people
const c = { ...people, name: 'ebiebi' }
console.log(Object.is(a,b)) // true !!!!!!!!!!!!!!!!!!!
console.log(Object.is(b,c)) // false !!!!!!!!!!!!!!!!!!!
console.log(people) // {name: "ebiebi", age: 11}
// setPeople(people) // 错误写法 页面数据不更新 状态不改变 State:{age: 11, name: "ebiebi"}
setPeople({...people}) // 正确写法
}
return (
<>
<button onClick={change}>setPeople</button>
{people.name}
{people.age}
</>
)
}
高级写法
<button onClick={() => setPeople({ ...people, age: people.age +1})}>setPeople-Age</button>
useEffect
useEffect就是一个Effect Hook,给函数组件增加了操作副作用的能力。它跟class组件中的 componentDidMount 、 componentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个API,可以让函数组件实现类组件部分生命周期的功能
useEffect(callback, dependencies)
第一个参数:接受一个函数,可以来做一些副作用比如异步请求,修改外部参数等行为。
- 该回调函数返回值(如果有):则在组件销毁或者调用函数前调用 相当于componentWillUnmount
第二个参数:是一个数组,如果数组中等值变化来才会触发执行useEffect第一个参数中的函数。
- 数组是空数组 只有在组件初始化或销毁的时候才会触发,代替 componentDidMount 慎用(会报警告建议你删除依赖数组)
- 数组中是state或者props 相当于componentDidUpdate
- 不传递第二个参数,每次渲染DOM之后都会执行第一个参数的函数。
官网案例:useEffect( ( )=>{ } )下面这个组件在 React 更新 DOM 后会设置一个页面标题
```typescript import React, { useState, useEffect } from ‘react’;
export default function App() { const [count, setCount] = useState(0);
// 相当于 componentDidMount 和 componentDidUpdate: 开始执行一次,组件更新执行
useEffect(() => {
// 使用浏览器的 API 更新页面标题
console.log(‘执行’)
document.title = You clicked ${count} times
;
});
return (
You clicked {count} times
当你调用useEffect 时,就是告诉React在完成DOM的更改后运行你的“副作用”函数。每次渲染后都会调用副作用函数——包括第一次渲染的时候。
<a name="x85aO"></a>
##### 案例:useEffect( ( )=>{ } , [ ] )
结论:组件第一次渲染会执行一次,可以获得真实DOM,监听了count的状态,状态改变触发“副作用”函数,可以用于**对某个数据的监听**<br />点击第二个button按钮不会触发“副作用”函数
```typescript
import React from 'react'
export default function App() {
const [count, setCount] = React.useState(0);
const [people, setName] = React.useState({ name: 'syukinmei', age: 18 })
// 相当于 componentDidMount 和 componentDidUpdate:
React.useEffect(() => {
console.log('执行') // 第一次渲染会执行
console.log(document.querySelector('button')) // 可以获得真实DOM
}, [count]);
return (
<div>
<button onClick={() => setCount(count + 1)}> You clicked {count} times </button>
<button onClick={() => setName({ ...people, age: people.age + 1 })}> {people.name} is {people.age} years old </button>
</div>
);
}
案例:React.useEffect( () => { return ()=>{ } } );
import React from 'react'
export default function App() {
const [Flag, setFlag] = React.useState(true);
// 相当于 componentDidMount 和 componentDidUpdate:
React.useEffect(() => {
console.log('App执行') // 第一次渲染会执行
console.log('App',document.querySelector('h1')) // 可以获得真实DOM
return ()=>{
console.log('App组件被销毁了')
}
});
return (
<div onClick={() => { setFlag(!Flag) }}>
<h1 >title</h1>
{ Flag && <Child />}
</div>
);
}
function Child() {
React.useEffect(() => {
console.log('Child执行') // 第一次渲染会执行
console.log('Child', document.querySelector('h1')) // 获得的是App组件中的h1标签???
return ()=>{
console.log('Child组件被销毁了')
}
});
return (
<div>
<h1>Child组件</h1>
</div>
)
}
- 执行结果:
- 正常写法:当Child组件销毁 执行Child组件中的“副作用”函数的返回值
```typescript
import React from ‘react’
export default function App() {
const [Flag, setFlag] = React.useState(true);
return (
); }<div onClick={() => { setFlag(!Flag) }}>
<h1 >title</h1>
{ Flag && <Child />}
</div>
function Child() { React.useEffect(() => { return ()=>{ console.log(‘Child组件被销毁了’) } }); return (
Child组件
<a name="ZtRXl"></a>
##### 案例:使用副作用函数实现 组件销毁阶段消除计时器,消除绑定在window或Document身上的事件,删除第三方库实例
```typescript
import React from 'react'
import Swiper from 'swiper'
export default function App() {
const [Flag, setFlag] = React.useState(true);
return (
<div onClick={() => { setFlag(!Flag) }}>
<h1 >title</h1>
{ Flag && <Child />}
</div>
);
}
function Child() {
let timer: any = null // 用于销毁计时器
let swiper: any = null
console.log(timer)
React.useEffect(() => {
timer = setInterval(() => {
console.log('计时器')
}, 1000)
window.onresize = function () {
console.log('可视窗口尺寸改变了')
}
swiper = new Swiper('container')
return () => {
console.log('Child组件被销毁了')
// 删除计时器
clearInterval(timer)
// 删除绑定在window/document身上的事件
window.onresize = null
// 清除第三方库实例化
swiper = null
}
});
return (
<div>
<h1>Child组件</h1>
<div className="container"></div>
</div>
)
}