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,状态的初始值为syukinmeiconst [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?: stringage?: number}const [people, setPeople] = React.useState<People>({ name: 'syukinmei', age: 11 })const change = () => {// 方法用于书写修改name的逻辑// setPeople({// name:'ebibei'// })const a = peoplepeople.name = 'ebiebi'const b = peopleconst 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按钮不会触发“副作用”函数```typescriptimport 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')) // 可以获得真实DOMreturn ()=>{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身上的事件,删除第三方库实例```typescriptimport 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 = nullconsole.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>)}
