你对 React 了解多少?看看大家常犯的错误
低垂的果实
根据我采访 React 开发人员的经验,有些东西是许多开发人员不知道的,但很容易学习,并且可以帮助他们成为更好的 React 开发人员,并处理怪癖和错误。
它们是关于如何使用 React 以及 React 如何工作的知识片段。
准备好了吗?以下是问题:
如果我们手动更改 DOM,React 在渲染应用程序时会考虑到这一点吗?
要回答这个问题,你需要了解虚拟 DOM 的工作原理。
简而言之,虚拟 DOM 是 DOM 树状态的表示,以纯 JavaScript 对象的形式呈现。
当 React 渲染我们的应用程序时,它会渲染新的虚拟 DOM,并将其与之前的虚拟 DOM进行比较。请注意,在这个阶段,它根本不会查看 DOM 的当前状态。
意思是 - 如果您手动更改 DOM,React 将忽略这些更改,并且/或者在重新渲染该元素时覆盖它们。
什么原因导致组件重新渲染?
很多人给出的答案是——要么是状态改变,要么是 props 改变,要么是 context 改变。
更正确的答案是——状态改变,要么是父组件重新渲染,要么是 context 改变。
默认情况下,如果父组件渲染,React 也会渲染所有子组件,即使之前的 props 完全相同。
属性的比较只发生在纯组件,或者实现了 Memo/shouldComponentUpdate 的组件中。
什么是功能组件?
出于某种原因,许多开发人员认为所有函数式组件都是纯粹的。事实并非如此。函数式组件过去是无状态的,但现在有了钩子,情况也发生了变化。因此,函数式组件和类组件之间的主要区别仅在于语法,并且只能在函数式组件内部使用钩子。
此外,有些操作只能在类组件中执行,例如定义 ErrorBoundaries。
组件名称必须以大写字母开头吗?
这是一个棘手的问题。虽然单个变量必须以大写字母开头才能作为组件,但如果使用嵌套属性,则也可以以小写字母开头。以下语法有效:
comps = { myComp: () => <div>hi</div> }
...
return <comps.myComp/>
状态是否异步更新?
这是一个非常棘手的问题,特别是因为 React 文档确实说它是异步的。
以此代码为例:
function MyComp() {
console.log('render');
const [counter, setCounter] = useState(0);
const onClick = () => {
setCounter(prev => prev + 1);
console.log('after setCounter');
}
return <button onClick={onClick}>Click me</button>;
}
日志内容将是:'after setCounter', 'render'。
所以看起来渲染确实是在状态更新后异步发生的。但是,如果我们在 Promise 解析后添加一个异步日志会怎么样呢?
function MyComp() {
console.log('render');
const [counter, setCounter] = useState(0);
const onClick = () => {
Promise.resolve().then(() => console.log('promise'));
setCounter(prev => prev + 1);
console.log('after setCounter');
}
return <button onClick={onClick}>Click me</button>;
}
现在日志将会是:'after setCounter', 'render', 'promise'。这意味着渲染是同步的(它发生在 promise 被解决之前)。
那么到底发生了什么?
它感觉上是异步的,因为 React 以批处理的方式执行事件处理程序。这意味着渲染函数只会在事件处理程序完成后调用。所有状态更新都会在此之前排队。
因此,它可能感觉上是异步的,但它在事件处理程序完成后是同步发生的。
更复杂的是,在并发模式下,渲染可能真的是异步的。
所以 React 团队的建议是将状态更新视为始终异步的,我认为这是个好建议。
何时使用layoutEffect,何时使用effect?
布局效果很少受到关注,许多开发者并不完全理解它们。布局效果与效果的主要区别在于,布局效果在提交阶段(即实际 DOM 更改之后)同步发生,而效果是异步发生的。
那么什么时候应该使用布局效果呢?例如,当你想避免 UI 闪烁时。例如:
const ScrollSync = ({ scrollTop }) => {
const container = useRef();
useLayoutEffect(() => {
container.current.scrollTop = scrollTop;
}, [scrollTop]);
return <div ref={container}>...</div>
}
我们使用布局效果将元素的滚动位置绑定到状态,以确保它同步发生并且不会造成任何延迟。
可以从事件处理程序保存对事件参数的引用吗?
答案是否定的。React
中的事件对象是可回收的。这意味着,将来,相同的事件引用可能会指向不同的事件。
如果必须这样做,请保存对事件或原生事件属性的引用,或者调用 event.persist() 将其从事件池中分离。
包起来
我希望你学到了一些新东西,或者至少对你已经知道的事情有了一些整理或新的看法。
如果您有任何反馈或我犯了错误 - 请在评论部分告诉我。
鏂囩珷鏉yu簮锛�https://dev.to/adamklein/how-knowledgable-you-are-about-react-see-common-mistakes-people-make-5ha4