root.render(<P4 id={1}></P4>);
状态
先来看一个例子,能否把服务器返回的数据显示在页面上
import axios from 'axios'
let count = 0
export default function P5(props: { id: number }) {
function getTime() {
const d = new Date()
return d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds()
}
async function updateStudent() {
const resp = await axios.get(
`http://localhost:8080/api/students/${props.id}`
)
Object.assign(student, resp.data.data)
console.log(current, student, getTime())
}
const current = count++
let student = { name: 'xx' }
console.log(current, student, getTime())
updateStudent()
return <h3>姓名是:{student.name}</h3>
}
-
count 是一个全局变量,记录 P5 函数第几次被调用
执行效果,控制台上
0 {name: 'xx'} '16:22:16'
0 {id: 1, name: '宋远桥', sex: '男', age: 40} '16:22:18'
此时页面仍显示 姓名是:xx
那么修改一下 props 的 id 呢?进入开发工具把 id 从 1 修改为 2,控制台上
1 {name: 'xx'} '16:24:0'
1 {id: 2, name: '俞莲舟', sex: '男', age: 38} '16:24:2'
此时页面仍显示 姓名是:xx
为什么页面没有显示两秒后更新的值?
-
第一次,页面显示的是 P5 函数的返回结果,这时 student.name 还未被更新成宋远桥,页面显示 xx
-
虽然 2s 后数据更新了,但此时并未触发函数执行,页面不变
-
-
第二次,虽然 props 修改触发了函数重新执行,但既然函数重新执行,函数内的 student 又被赋值为
{ name: 'xx' }
,页面还是显示 xx-
2s 后数据更新,跟第一次一样,并未重新触发函数执行,页面不变
-
结论:
useEffect
Effect 称之为副作用(没有贬义),函数组件的主要目的,是为了渲染生成 html 元素,除了这个主要功能以外,管理状态,fetch 数据 ... 等等之外的功能,都可以称之为副作用。
useXXX 打头的一系列方法,都是为副作用而生的,在 react 中把它们称为 Hooks
useEffect 三种用法
-
函数是无状态的,执行完毕后,它内部用的数据都不会保存下来
-
要想让函数有状态,就需要使用 useState 把数据保存在函数之外的地方,这些数据,称之为状态
-
useState
-
import axios from 'axios' import { useReducer, useState } from 'react' import { Student } from '../model/Student' let count = 0 export default function P5(props: { id: number }) { // ... async function updateStudent() { const resp = await axios.get( `http://localhost:8080/api/students/${props.id}` ) Object.assign(student, resp.data.data) console.log(current, student, getTime()) } const current = count++ let [student] = useState<Student>({ name: 'xx' }) console.log(current, student, getTime()) updateStudent() return <h3>姓名是:{student.name}</h3> }
接下来使用 setXXX 方法更新 State
-
import axios from 'axios' import { useState } from 'react' import { Student } from '../model/Student' export default function P5(props: { id: number }) { async function updateStudent() { const resp = await axios.get(`/api/students/${props.id}`) setStudent(resp.data.data) } let [student, setStudent] = useState<Student>({ name: 'xx' }) updateStudent() return <h3>姓名是:{student.name}</h3> }
工作流程如下
首次使用 useState,用它的参数初始化 State
2s 后数据更新,setStudent 函数会更新 State 数据,并会触发下一次渲染(P5 的调用)
再次调用 useState,这时返回更新后的数据
这时再返回 jsx,内容就是
姓名是:宋远桥
了P.S.
使用了 useState 之后,会执行两次 xhr 请求,后一次请求是 react 开发工具发送的,不用理会
问题还未结束,第二次 P5 调用时,updateStudent 还会执行,结果会导致 2s 后响应返回继续调用 setStudent,这会导致每隔 2s 调用一次 P5 函数(渲染一次)
如何让 updateStudent 只执行一次呢?一种土办法是再设置一个布尔 State
接下来数据更新
第二次进入 P5 函数时,由于 fetch 条件不成立,因此不会再执行两个 setXXX 方法
函数式组件的工作流程
-
首次调用函数组件,返回的 jsx 代码会被渲染成【虚拟 dom 节点】(也称 Fiber 节点)
-
此时使用 useState 会将组件工作过程中需要数据绑定到【虚拟 dom 节点】上
-
根据【虚拟 dom 节点】会生成【真实 dom 节点】,由浏览器显示出来
-
-
当函数组件的 props 或 state 发生变化时,才会重新调用函数组件,返回 jsx
-
props 变化由父组件决定,state 变化由组件自身决定
-
jsx 与上次的【虚拟 dom 节点】对比
-
如果没变化,复用上次的节点
-
有变化,创建新的【虚拟 dom 节点】替换掉上次的节点
-
-