先来看段代码
setLoading(true)
changeImgSrc() // 将所有webp格式的URL转成png格式
setLoading(false)
这段代码的意思是,初始化的时候,显示loading,然后执行改变img的URL的异步操作(img通过数组map生成),执行完成后loading变为false。出现的问题是,img的URL在改变之后,又变回了之前的URL。
初步设想是setLoading导致render,render的时候img的src又被重置了。好像挺正常的,但是仔细看官网diff的规则,发现并不是这样。官网中对于diff的规则有几条:
1.当元素类型变化时,会销毁重建
2.当元素类型不变时,对比属性
3.当组件元素类型不变时,通过props递归判断子节点
4.递归对比子节点,当子节点是列表时,通过key和props来判断。如果key一致,则更新和props相关的部分,若key不一致,就销毁重建
对比发现,第4条符合现在的情况,img的URL是通过数组渲染出来的,渲染前后元素没变,数组没变。但是key使用的是math.random(),所以key每次都是新的。之所以使用math.random(),是因为在组件中渲染了两个相同的块。类似这样:
const a = [1,2,3,4]
<div>
<p>
{
a.map((it,index) => <span key={index + Math.random()}></span>)
}
</p>
<p>
{
a.map((it,index) => <span key={index + Math.random()}></span>)
}
</p>
</div>
我之前认为,两个p元素使用相同的数组来map,如果key都使用index的话会有问题。但是react官网中写到,react中的key只是在兄弟节点中必须唯一。
这样,因为我使用了Math.random(),导致每次渲染的时候,组件就会卸载,重新生成。
而如果我用了唯一的确定的值,img元素就不会被卸载,并且img是不依赖于props的,所以react会认为img元素没有变化。也就不会重新生成。
下面这个例子,可以更直观的说明key使用index、Math.random()、唯一确定值的区别
const [a, setA] =useState([1,2,3,4])
const handleClick = () => {
setA([2,3,4,1])
}
<div>
{
a.map(it =>
<div key={it}>
it
<input type="radio" />
</div>
)
}
<button onClick={handleClick}>乱序</button>
</div>
当key是唯一确定的值时,对input做的操作(比如选中)在点击按钮之后,会保留,并且input当顺序随着数组的变化而变化
当key是index时,对input做的操作在点击按钮之后,会保留,但是input的顺序不变
当key是Math.random()时,对input做的操作不会保留,也就是每次点击都会初始化