如何优化状态变化引起的react re-renders?
Posted
技术标签:
【中文标题】如何优化状态变化引起的react re-renders?【英文标题】:How to optimize react re-renders caused by state changes? 【发布时间】:2021-11-21 06:29:03 【问题描述】:我的 UI 上有一个树状结构。让我们想象它们是这样的:
这些组件上有按钮,用户可以在 A 中动态添加更多 B 组件,在每个 B 中添加 C 组件。底层数据结构看起来非常相似,都是非规范化格式,如下所示:
"compA":
"Aproperty1": "something",
"Aproperty2": 5,
"Bcomponents": [
"Bproperty": 13,
"Ccomponents": []
...
]
每个组件在这个结构中操作自己的部分,用唯一的 ID 标识正确的部分。
数据之前已经存储在 redux 存储中,现在我正在尝试使用 GraphQL 和 Apollo 客户端,并且我正在使用 Reactive 变量,但我认为这是同一个问题。每当我在 C 组件中移动滑块时,它都会更新反应变量(或 redux 存储),并且由于 A 组件和所有 B 组件都使用相同的变量,因此整个树都会重新渲染。由于我们谈论的是滑块,它会导致大量更新,并且实际上使它变得滞后。我对滑块使用了 debounce,因此它会发出大部分更新,但这不是根本原因,我觉得这不是正确的解决方法。
归根结底,我需要组件 A 中可用的全部数据,因为它可以将其提交到后端。
我可能会通过规范化数据并在提交时对其进行非规范化使其变得更好一些,因为修改它会更快,但重新渲染问题仍然存在,我想这会占用更多资源。
当我在 C 组件中调整滑块时,解决此问题并避免重新渲染整个树的正确方法是什么?
【问题讨论】:
如果你用 redux 向上传递数据,它会重新渲染各自的父元素,但如果你向下传递它,只会重新渲染子元素。我建议向下传递提交函数(将数据提交到服务器),这样孩子根本不需要传递数据 你可以有不同的组件然后调用,每个组件都可以有不同的 useEffect 并根据要求传递道具 @kmp 是的,将函数向下传递会很好,但接下来会调用它吗?我认为提交功能唯一合乎逻辑的地方是在组件A中。数据需要以单件形式提交,所以要么我不明白你的意思,要么孩子不可能不传递任何数据。跨度> 我的意思是使用上下文而不是 redux,因为据我了解您的问题,您希望以最少的渲染提交 1 块数据。您的应用程序可以访问提交函数和上下文中的所有必要数据,然后这些数据可以被子进程使用,因此它将是唯一的渲染过程,最大限度地减少资源的使用 啊,我明白了,谢谢你的澄清。现在这似乎是一个很好的方法。如果您花时间将其转换为答案,我会接受它作为一个很好的答案 【参考方案1】:对不起,笨拙的代码块..我累了,但我希望它很清楚。告诉我。
如果您需要以任何方式调整它,也可以使用docs(显然)。这只是一种基本的方法,更像是结构性的然后是实用的。
// Context declaration
const MyContext = React.createContext(
submitFn: () => Promise<void>, // -> this will be sent to the server
changeDataFn: (data: any) => any, // -> this is required to change the data
data: any // -> this is the data
);
// Don't be confused, it's not quite TypeScript
// needed types for clarity
...
// Wrap ComponentA with your context
<MyContext.Provider value=/* define your functions bodies and default values */>
<ComponentA />
</MyContext.Provider>
...
// Component A
export function ComponentA(props)
// Use MyContext.Consumer wherever you want to consume data
// or you can use `useContext` instead of `Consumer` if you prefer
// You may add data here if you have any
// eg. ctx.changeData("Hello World")
return <MyContext.Consumer>
ctx => /* render Component A stuff */
</MyContext.Consumer>
...
// Component B
export function ComponentB(props)
// You may add data to the context in Component B
// using the changeData function
return <MyContext.Consumer>
ctx => /* render Component B stuff */
</MyContext.Consumer>
...
// Component C
export function ComponentC(props)
// Finally make your api call here,
// it should re-render this component only
// eg. ctx.submitFn().then(() => console.log("Yeeey it works!"))
return <MyContext.Consumer>
ctx => /* render Component C stuff */
</MyContext.Consumer>
// you don't have to consume the context in each children
// it's there to wrap your component in the context,
// so it can access the stored data in it
【讨论】:
太棒了,非常感谢!以上是关于如何优化状态变化引起的react re-renders?的主要内容,如果未能解决你的问题,请参考以下文章
[react] react中的setState缺点是什么呢?