做五金建材市场的网站,网站游戏网站开发,网站哪类业务建设投入会带来间接收益,蛇口网站建设公司文章目录 react setState函数useState()钩子创建state如何根据props更新state值 setState的参数是下一个状态statesetState的参数是更新函数functionsetState异步与同步合成事件setState 实现原理 react setState函数
useState()钩子创建state
const [state, setState] useS… 文章目录 react setState函数useState()钩子创建state如何根据props更新state值 setState的参数是下一个状态statesetState的参数是更新函数functionsetState异步与同步合成事件setState 实现原理 react setState函数
useState()钩子创建state
const [state, setState] useState(initialState)
useState钩子函数向组件添加一个状态变量该函数返回状态变量(只用于显示)与设置状态变量的setState函数。
useState的使用细节
调用修改函数修改state变量的值(state值发生变化)会触发组件的重新渲染直接修改state变量不会触发组件的重新渲染。组件重新渲染等同于函数重新执行函数体的代码会再次调用。执行useState时会返回本次渲染的state值。setState()是由钩子函数useState()生成的useState()会保证数组的每次渲染都会获取到相同的setState()React只在初次渲染时保存初始状态后续渲染将其忽略。
参数的两种形式 初始化函数 惰性初始化函数:如果useState的参数是一个生成数据的函数则可以直接将这个函数本身传递仅在初始化期间调用一次不是函数()这种情况每次渲染时调用此函数 如果函数需要传参可以在useState之前使用useMemo缓存该函数的结果。 参数是值 - 如果是props需要注意同步的问题
如何根据props更新state值
为什么父组件传递的props改变子组件state值不同步更新
父组件传递给子组件的props发生改变引发子组件的render并没有执行子组件的constructor函数子组件没有被卸载自然不会重新加载只会重新render。父组件的props传递给子组件的state子组件的state只会在第一次加载的时候被赋值后续的父组件props变化并不会被赋值到子组件的state上
解决办法 子组件使用useEffect() useEffect的执行时机组件挂载-state改变-DOM改变-绘制屏幕-useEffect所以如果在useEffect里更新state的数据会在屏幕绘制成功后又重新开始这个流程。 尽量不使用官方解释https://zh-hans.react.dev/learn/you-might-not-need-an-effect // 方法1子组件使用useEffect
const [tags, setTags] useState(() props.tags);useEffect(() {
setTags(props.tags);
}, [props.tags]);父组件使用子组件时绑定key。当父组件要传递的props改变时改变key值将子组件认为是不同的子组件(引发子组件重新加载而不是单纯的渲染) –推荐 children userId{userId} key{userId}/setState的参数是下一个状态state
在 React 中状态被认为是只读的因此应该替换它而不是改变现有对象。当state值是一个对象时setState(state)修改的参数由于是下一次渲染的state值(组件重渲染后useState返回state值)需要使用新的对象去替换已有对象。
// user是对象对象的地址没有发生变化所以不会引起组件重新渲染
const [user, setUser] useState({name:ranran,age:18})
user.name xxx;
setUser(user);// 解决方案:将其拷贝给另一个新对象修改新对象的属性
setUser({...user,name:xxx}) // 后面的name会覆盖前面的namesetState()去修改一个state时并不表示修改当前的state修改的是组件下一次渲染的state(下一次 渲染中useState返回的内容)
说明案例1 假设name的初始值是Taylor点击按钮后触发handleClick 函数修改name的值打印发现是修改前的值Taylor。
// 点击按钮修改名字
const handleClick () {setName(Robin); // 下一次渲染中useState返回的内容console.log(name); // Still Taylor! 本次渲染useState返回的内容
}说明案例2 假设 age 为 42这个处理函数三次调用 setAge(age 1)点击一次后age 将只会变为 43。
const handleClick() {setAge(age 1); // setAge(42 1)setAge(age 1); // setAge(42 1)setAge(age 1); // setAge(42 1)
}setState的参数是更新函数function
更新函数的参数是待定状态的state。
const handleClick () {setAge(a a 1); // setAge(42 43)setAge(a a 1); // setAge(43 44)setAge(a a 1); // setAge(44 45)
}React 将更新函数放入 队列 中。然后在下一次渲染期间(组件重渲染useState返回state的过程中)它将按照相同的顺序调用 1.a a 1 将接收 42 作为待定状态并返回 43 作为下一个状态。 2.a a 1 将接收 43 作为待定状态并返回 44 作为下一个状态。 3.a a 1 将接收 44 作为待定状态并返回 45 作为下一个状态。 按照惯例通常将待定状态参数命名为状态变量名称的第一个字母如 age 为 a。然而你也可以把它命名为 prevAge 或者其他你觉得更清楚的名称。 批量更新 多个顺序的setState不是同步地一个一个执行会一个一个加入队列然后最后一起执行。在异步setState更新队列时存储的是合并状态上述案例age的最终状态45。因此前面设置的 key 值会被后面所覆盖最终只会执行一次更新。
setState异步与同步
明确这里所说的同步和异步指的是 API 调用后更新 DOM 是同步还是异步的。 react18之后已经全部异步了
设计为异步更新的原因
setState设计为异步可以显著的提升性能如果每次调用setState都进行一次更新那么意味着render函数会被频繁调用界面重新渲染这样效率是很低的最好的办法应该是获取到多个更新之后进行批量更新这也为什么在react可控范围类希望setState是异步的。如果同步更新了state但是还没有执行render函数而且props依赖于state中的数据那么state和props不能保持同步state和props不能保持一致性会在开发中产生很多的问题。
setState是同步还是异步呢
在React内部机制能检测到的地方组件生命周期(除componentDidUpdate) 或React合成事件中setState是异步。如果 setState 在原生 JavaScript 控制的范围被调用setState是同步。
setState 的“异步”并不是说内部由异步代码实现其实本身执行的过程和代码都是同步的只是合成事件和钩子函数的调用顺序在更新之前导致在合成事件和钩子函数中没法立马拿到更新后的值形成了所谓的“异步”。
合成事件
在 React 中为元素添加的事件被叫做合成事件区分一下js的原生事件
合成事件的好处
屏蔽了浏览器之间关于事件处理的兼容性问题为合成事件对象内部提供了统一的 API。提升性能, React 并不会将事件添加到真正的DOM 元素上事件都被委托给 document 。
步骤说明 1.React 会在拥有事件的 DOM 对象身上添加一个 store 对象在 store 对象中存储事件名称及事件处理函数然后通过document 分发事件。 2.当事件被触发后通过获取事件源对象查看事件源对象中是否存在store对象获取 store 对象中事件处理函数执行事件处理函数。
核心实现 react 中所有的合成事件都会经过dispatchEventForLegacyPluginEventSystem()处理其中通过设置全局变量isBatchingEventUpdates来标志当前的变化是否发生在React的可调度范围内。
/* 所有的事件都将经过此函数统一处理 */
function dispatchEventForLegacyPluginEventSystem(){// handleTopLevel 事件处理函数 batchedEventUpdates 批量更新batchedEventUpdates(handleTopLevel, bookKeeping);
}
function batchedEventUpdates(fn,a){/* 开启批量更新 */isBatchingEventUpdates true;try {/* 这里执行了的事件处理函数 比如在一次点击事件中触发setState,那么它将在这个函数内执行 */return batchedEventUpdatesImpl(fn, a, b);} finally {/* 完成一次事件批量更新 */isBatchingEventUpdates false;}
}setState 实现原理
当 setState 方法被调用后将状态传递给组件更新器让组件更新器将状态临时存储起来。每个组件都会有自己的组件更新器当需要更新组件时调用组件更新器。状态临时保存完成后判断当前是否为批量更新模式如果是将组件更新器添加到更新队列中如果不是直接更新组件。 当触发合成事件时, 在事件处理函数执行之前会先将批量更新模式设置为 true然后执行事件处理函数收集状态。当事件处理函数执行完成后执行批量更新操作从更新队列中获取组件更新器并调用。组件更新器调用完成后再将批量更新模式设置为 false。更新组件时先判断是否有状态需要更新如果有就先计算最新状态将得出的最新状态重新设置给组件(useState的返回值)。 计算状态时如果状态是函数类型调用函数传入当前状态返回最新状态。如果状态是对象类型使用对象状态覆盖原有状态。组件状态计算完成后通过调用组件内部的 render 方法获取新的 VirtualDOM再通过 DOM 对象获取旧的虚拟 DOM然后调用 diff 方法进行比对对比完成后将差异更新到真实 DOM 对象中。