我有一个要在列表中呈现的项目列表(openItems(。对于列表中的每个项目,我都需要一个输入来更改打开项目的一个属性(持续时间(。问题是,每次更改属性时,父项(openItems(及其所有子项(包括输入(都会重新调用。这会使输入失去焦点。
我正试着思考这个模式应该如何实现(使用功能组件(,以避免崩溃。我尝试使用React.memo
,但随着数据的变化,这没有什么区别。
下面是它当前实现方式的精简代码片段。(我使用的是Antd组件(
const Page = () => {
const dispatch = useDispatch();
const openItems = ...get items from state
const setMs = (id: string, ms: number) => {
dispatch(changeDuration({ id, duration: ms }));
}
const InputMs: React.FC<{ row: RowInterface }> = (props) => (
<InputNumber
defaultValue={props.row.durationMs}
onChange={(v) => setMs(props.row.id, v)}
/>
)
const RowContent: React.FC<{ row: RowInterface }> = (props) => (
<>
<SmallText>Duration in ms:</SmallText>
<InputMs row={props.row} />
</>
);
const Rows: React.FC<RowProps> = (props) => {
return (
<>
{props.data.map((row) =>
<RowContent row={row} />
)}
</>
)
};
return( <Rows data={openItems} /> )
};
export default Page;
基于这个答案,我还尝试将输入作为一个单独的组件,但结果相同:
const InputMs: React.FC<{ row: RowInterface }> = (props) => {
const dispatch = useDispatch();
const setMs = (id: string, ms: number) => {
dispatch(changeDuration({ id, duration: ms }));
}
return (
<InputNumber
defaultValue={props.row.durationMs}
onChange={(v) => setMs(props.row.id, v)}
/>
)
}
const Page = () => {
const openItems = ...get items from state;
const RowContent: React.FC<{ row: RowInterface }> = (props) => (
<>
<SmallText>Duration in ms:</SmallText>
<InputMs row={props.row} />
</>
);
const Rows: React.FC<RowProps> = (props) => {
return (
<>
{props.data.map((row) =>
<RowContent row={row} />
)}
</>
)
};
return (<Rows data={openItems} />)
};
export default Page;
现在我已经决定对输入进行临时更新。(在这里找到解决方案(
CCD_ 2现在改变本地状态,并且CCD_。通过这种方式,用户可以按预期使用输入。
const InputMs: React.FC<{ row: RowProps}> = (props) => {
const [value, setValue] = useState<number>();
const dispatch = useDispatch();
const setState = (id: string) => {
if(value){
dispatch(changeDuration({ id, duration: value }));
setValue(undefined);
}
}
return (
<InputNumber
defaultValue={props.row.durationMs}
onChange={(v) => setValue(v)}
onBlur={(v) => setState(props.row.id)}
/>
)
}
这里发生的事情是,您正在另一个组件内部定义子组件。这意味着,当Page
由于redux状态的更改而重新渲染时,您的InputMs
、RowContent
和Rows
组件都会重新创建。它们将卸载和重新装载,并失去集中状态,因为它现在是一个全新的组件实例。
你已经走到了一半,因为你已经找到了告诉你这一点的链接答案。但是必须移动所有内部组件。您只移动了InputMs
,但仍在重新创建的Rows
和RowContent
内部调用它。
您希望在文件的顶层定义所有组件。
也可以将这些代码段编写为作为函数调用的普通函数,而不是作为JSX元素调用它们。例如:
const Page = () => {
const openItems = ...get items from state;
const renderRow = (row: RowInterface) => (
<>
<SmallText>Duration in ms:</SmallText>
<InputMs row={row} />
</>
);
return (
<>
{openItems.map(renderRow)}
</>
);
}