如何优化由状态变化引起的反应重新渲染?

How to optimize react re-renders caused by state changes?

我的 UI 上有一个树状结构。让我们想象他们是这样的:

这些组件上有按钮,用户可以在每个 B 中的 A 和 C 组件中动态添加更多 B 组件。底层数据结构看起来非常相似,它的非规范化格式如下所示:

{
  "compA": {
    "Aproperty1": "something",
    "Aproperty2": 5,
    "Bcomponents": [
      {
        "Bproperty": 13,
        "Ccomponents": []
      }
      ...
    ]
  }
}

每个组件在此结构中操纵自己的部分,用唯一 ID 识别正确的部分。

数据以前存储在 redux 存储中,现在我正在试验 GraphQL 和 Apollo 客户端,我正在使用 Reactive 变量,但我认为这是同样的问题。每当我在 C 组件中移动滑块时,它都会更新反应变量(或 redux 存储),并且由于 A 组件和所有 B 组件都使用相同的变量,因此整个树都会重新渲染。由于我们谈论的是滑块,它会导致大量更新,并且实际上会使其变得滞后。我对滑块使用了去抖动,因此它发出了大部分更新,但这不是根本原因,我觉得这不是正确的解决方法。

一天结束时,我需要组件 A 中可用的全部数据,因为这是可以将其提交到后端的那个。

我可能可以通过规范化数据并在提交时对其进行非规范化来使它变得更好一些,因为修改它会更快,但重新渲染问题仍然存在,我想这会占用更多资源。

当我在 C 组件中调整滑块时,解决此问题并避免重新渲染整个树的正确方法是什么?

抱歉笨拙的代码块..我很累,但我希望它很清楚。让我知道。

如果您需要以任何方式对其进行调整,也可以使用 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