我有一个可重用的组件<Layout />
,它允许我将自定义组件传递到content
和sidebar
属性中。
在其中一个实例中,我将一个输入字段放入内容中,该内容是一个受控组件,并使用useState()
来处理数据。
但是,每当我在输入中键入时,我发现键盘都会失去焦点。似乎整个表单都在重新渲染,我无法阻止它这样做。
当我的代码全部内联时,这很好,但是自从重构它以使用<Layout />
组件以来,它产生了一些奇怪的效果。
- 我不想使用核心 React 16.x 以外的任何额外库
- 我尝试使用
useCallback()
,键,名称,ID,引用 - 都无济于事 - 我已经将回调函数移出了范围,并根据这个答案传递了值,但问题仍然存在
import React, { useState } from 'react';
function Layout({ content: Content, sidebar: Sidebar }) {
return (
<>
<div>
<Content />
</div>
<div>
<Sidebar />
</div>
</>
);
}
function Page() {
const [value, setValue] = useState('');
return (
<>
<Layout
content={() => (
<input
value={value}
onChange={(event) => setValue(event.target.value)}
/>
)}
sidebar={() => (
<p>Lorem ipsum...</p>
)}
/>
</>
);
}
export default Page;
你的问题是你正在定义匿名函数并将它们视为 React 组件,这在许多情况下很好,但这里会导致 React 失去对这些函数中底层内容的理解。这意味着即使向输入添加 refs 或键也不会做任何事情,因为外部作用域每次只会看到一个全新的函数。
考虑一下:与其写<Content />
,不如写<>{Content()}</>
这将得到相同的结果,但表明每次渲染 Layout 时,它对其属性有一个完全不同的视图,因此每次都重新渲染整个事物。由于输入正在被新的但不同的输入快速替换,因此浏览器在框中失去焦点。但它也可能导致大量其他问题。
相反,将<Content />
替换为{content}
并且不要传递匿名函数 - 而只是按原样传递原始 JSX,它将不再重新呈现子级。
import React, { useState } from 'react';
function Layout({ content, sidebar }) {
return (
<>
<div>
{content}
</div>
<div>
{sidebar}
</div>
</>
);
}
function Page() {
const [value, setValue] = useState('');
return (
<>
<Layout
content={(
<input
value={value}
onChange={(event) => setValue(event.target.value)}
/>
)}
sidebar={(
<p>Lorem ipsum...</p>
)}
/>
</>
);
}
export default Page;
Content
在Layout
中渲染为 react 组件时,每次Page
重新渲染时,都会创建一个新的content
实例和sidebar
函数,因此 react 会在函数引用更改时重新挂载组件,因为函数在 react 组件内的每个渲染上都会重新创建,因此输入焦点丢失。
您可以使用content
和sidebar
组件作为渲染道具。渲染道具是组件用来知道要渲染的内容的函数道具。调用Layout
内部的函数来渲染Content
和Sidebar
。
function Layout({ content: Content, sidebar: Sidebar }) {
return (
<>
<div>{Content()}</div>
<div>{Sidebar()}</div>
</>
);
}
function Page() {
const [value, setValue] = useState("");
return (
<>
<Layout
content={() => (
<input
value={value}
onChange={event => setValue(event.target.value)}
/>
)}
sidebar={() => <p>Lorem ipsum...</p>}
/>
</>
);
}