当存在大状态但只有一个更改时,如何减轻无用的React渲染



我有一个搜索查询组件。用户键入query,单击run并执行它。但是,由于状态的组织方式,它会导致过多的重新呈现。以下是对它的简化。这种代码组织对我来说很自然。QueryEditor通常是一个复杂的编辑器,因此它存储自己的值。查询结果类似。我得到了每次更改的值,更新了父组件中的查询。然而,这会导致父组件中的所有HTML元素重新呈现,即使它们的状态没有改变。

首先,对概念不是很熟悉;

  • 这是预期行为吗?我所做的可能并不理想,但由于父状态的改变而导致每个DOM elment重新出现对我来说是不必要的。或者我可能做错了什么。

  • 如何改进?显然,需要改进的不是将每一个更改都作为回调,但我并没有找到一个好的方法来从父组件调用子组件的方法到getValue((。

如果需要,请链接:https://codesandbox.io/s/purple-architecture-5kpx5?file=/src/App.tsx您可以清楚地看到React DevTools对性能的影响,并看到渲染时间随着元素数量的增加而增加,即使元素没有改变,但查询文本也会改变。

function App() {
console.log("Re-render?");

const [name, setName] = useState("mustafa");
const [query, setQuery] = useState("");
const [data, setData] = useState<number[]>([]);
const runQuery = () => {
console.log("using query...", query);
setData([...Array(data.length + 1000).keys()]);
};
return (
<div>
<h1>Hello {name}</h1>
<p>Please enter your query</p>
<QueryEditor
onChange={(e) => {
setQuery(e.target.value);
}}
/>
<br />
<button onClick={runQuery}>Run Query</button>
<hr />
<h4>Results</h4>
{data.length > 0 && <p>There are {data.length} results </p>}
{data.length > 0 && <DataResults data={data} />}
</div>
);
}
interface EditorProps {
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
}
function QueryEditor({ onChange }: EditorProps) {
return <textarea onChange={onChange} />;
}
function DataResults({ data }: { data: number[] }) {
return (
<div>
<ul>
{data.map((a) => (
<li key={a}>Item {a}</li>
))}
</ul>
</div>
);
}

useState将始终导致重新渲染。当您在文本区域中键入时,您会更新导致重新报价的查询(这是useState的正常行为(。在您的情况下,在单击按钮运行查询之前,您不会使用查询。您可以通过const query = useRef("")更改const [query, setQuery] = useState("");。然后您的QueryEditoronChange函数变为

{(e) => {
query.current = (e.target.value);
}}

这样你就不应该有所有这些重新渲染。PS:当你运行查询时,你必须使用query.current,而不仅仅是查询

您不能更改父级的重新渲染行为。这就是react的工作方式,当子对象必须重新渲染时,父对象必须重新呈现子对象,从而再次被调用。看一看文档,了解重新渲染背后的逻辑。

在这种情况下,你可以做的是将拥有大量数据的兄弟姐妹存储起来。

所以如果我这样做:

const DataResultsMemoized = React.memo(DataResults);

然后在父级中使用:

{data.length > 0 && <DataResultsMemoized data={data} />}

然后,您的兄弟姐妹不会在每次您键入文本框时重新渲染。

下面是代码沙箱示例,检查当您键入时,子级中的console.log是否不会再次出现

最新更新