React 虚拟 DOM 的差异检测机制(4)

function App() { const [persons, updatePersons] = useState(["tom", "david"]); return ( <div> <h3>set age for each person</h3> {persons.map((name, index) => { - return <Item key={index} name={name} />; + return <Item key={name} name={name} />; })} <div> <button onClick={() => { updatePersons(prev => ["lily", ...prev]); }} > add person </button> </div> </div> ); }

这里假设每条数据其 name 值是不一样的,所以将它作为列表元素的唯一标识。

修正 key 之后的正常表现

修正 `key` 之后的正常表现

问题的解决

回到文章开头的问题,就可以理解其表现了。

const data1 = [10, 20]; const data2 = [50, 20, 10];

默认情况下,React 使用 index 作为 key。

对于第一条数据,其值由 10 变化到 50,动画正常,当再次设置时,其由 50 变回到 10。因为组件并没有重新初始化,所以其初始值确实是 50,所以看到了由 50 到 10 这个缩减的动画。

而对于第二条数据,因为前后值没变化,执行动画的 setTimeout 都不会执行。

修正的方法可以为元素指定一个随机的 key,这样每次组件都会重新渲染,不会复用之前的状态。

function App() { const [data, setData] = useState(data1); return ( <div> <button onClick={() => { setData(prev => (prev === data1 ? data2 : data1)); }} > switch data </button> {data.map((score, index) => { return ( - <div> + <div key={Math.random()}> <Bar score={score} /> </div> ); })} </div> ); }

修正后的百分比柱状条效果

修正后的百分比柱状条效果

将 key 设置成随机值是不推荐的做法,因为这样 React 就没法在渲染过程中对组件进行重用的优化。但像这里的特殊情况,你需要知道的是其中的原理,然后清楚自己在这样做时的影响。

总结

虚拟 DOM 将操作浏览器 DOM 的成本一部分转嫁到了 JavaScript 中,即进行差异计算的成本。提高了渲染的效率,但某些情况下也会是一个坑。

需要注意的是,React 的差异算法高效性是在两个假设前提下进行的,

如果父元素不同,其子节点产生不同的树。

开发者可通过为元素指定 key 来标识元素的唯一性,提高 React 差异检测时的效率。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/1053ea46a5e1210b236490db6a294f48.html