逃脱方案
- 不必使用Effect来转换渲染所需的数据
function Form() {
const [firstName, setFirstName] = useState('Taylor');
const [lastName, setLastName] = useState('Swift');
// 🔴 避免:多余的 state 和不必要的 Effect
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(firstName + ' ' + lastName);
}, [firstName, lastName]);
// ...
}
可以将effect和fullName删掉,然后通过
const fullName = firstName + ' ' + lastName;
直接在渲染期间计算这个fullName,避免fullName用旧值执行了整个渲染过程,然后更新之后再次渲染一遍.
如何判断计算是昂贵的
- 一般来说如果创建或者循环遍历成千上万个对象的时候才会很消耗时间.这时候可以使用useMemo()来进行缓存!
那我们应该怎么判断计算是昂贵的?
console.time('筛选数组');
const visibleTodos = getFilteredTodos(todos, filter);
console.timeEnd('筛选数组');
当这个时间达到1ms或者更多的时候,我们可以把计算结果记忆起来是有意义的.
注意:useMemo不会让你第一次渲染变快,他只是缓存跳过了不必要的更新!
当props变化时重置所有state
const [comment, setComment] = useState('');
// 🔴 避免:当 prop 变化时,在 Effect 中重置 state
useEffect(() => {
setComment('');
}, [userId]);
// ...
}
这样是低效的,因为这个组件会用旧值进行渲染,然后再用新值重新渲染一遍,如果里面是嵌套很多层会更加麻烦,所以倒不如在组件里面在封装一层将key传递进去,告诉react他们不同
return (
<Profile
userId={userId}
key={userId}
/>
);
}
function Profile({ userId }) {
// ✅ 当 key 变化时,该组件内的 comment 或其他 state 会自动被重置
const [comment, setComment] = useState('');
// ...
}
当props变化的时候调整部分state
prop传递的参数发生变化,不能更新全部state,只想更新一部分,此时用useEffect也不太合适,可以使用if去判断items !== prevItems是否一样再去更新,虽然这种方式比 Effect 更高效,但大多数组件也不需要它
还有最有效的办法
function List({ items }) {
const [isReverse, setIsReverse] = useState(false);
const [selectedId, setSelectedId] = useState(null);
// ✅ 非常好:在渲染期间计算所需内容
const selection = items.find(item => item.id === selectedId) ?? null;
// ...
}
现在完全不需要 “调整” state 了。如果包含已选中 ID 的项出现在列表中,则仍然保持选中状态。如果没有找到匹配的项,则在渲染期间计算的 selection 将会是 null。行为不同了,但可以说更好了,因为大多数对 items 的更改仍可以保持选中状态。