【问题标题】:ReactJs - Useless re-renders when a context changes - HOW TO SOLVE IT?ReactJs - 上下文更改时无用的重新渲染 - 如何解决?
【发布时间】:2022-11-02 23:15:35
【问题描述】:

我目前正在这个网站上工作:https://virgile-hasselmann.vercel.app/。并且有一个我无法解决的问题,我制作了一个自定义光标和一个 CursorContext 以在悬停时更改其状态等...我注意到当上下文更改其状态时,我的应用程序的每个组件都会重新呈现.为什么这样 ?以及如何解决? 我听说过记忆 ContextProvider 正下方的组件,但它似乎不起作用(提供程序下方的组件是我的布局组件,没问题吗?)

这是我的应用程序组件,我怎样才能防止那些无用的重新渲染发生?

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <AuthProvider>
      <CursorProvider>
        <Layout>
          <Component {...pageProps} />
        </Layout>
      </CursorProvider>
    </AuthProvider>
  );
}

我已将布局包装到 React.memo HOC 中,但没有用:

export default React.memo(Layout);

我尝试在布局组件中使用 useMemo :

const Layout = ({ children }: Props): JSX.Element => {
  const { width } = useWindowSize();
  const memoizedJSX = useMemo(() => {
    return (
      <div id="App" className="relative">
        <Head>
          <title>Virgile Hasselmann</title>
          <meta
            name="description"
            content="Virgile Hasselmann, a video and photo portfolio"
          />
          <link rel="icon" href="/favicon.ico" />
        </Head>
        {width! > 768 && <CustomCursor />}
        <header className="fixed w-screen z-50">
          <Navbar />
        </header>
        <main className="min-h-screen">
          <div>{children}</div>
        </main>
      </div>
    );
  }, []);
  return memoizedJSX;
};

它也没有工作......帮助!

【问题讨论】:

  • 这就是上下文的工作方式。一旦状态改变,它将重新渲染它的所有孩子。您可以尝试仅使用 CursorProvider 包装鼠标组件,而将应用程序的其余部分留在其中。这样只有鼠标组件将在状态更改时重新呈现
  • 我可以假设&lt;CustomCursor /&gt; 是需要从&lt;CursorProvider/&gt; 更新的内容吗?你能敲出一个简单的例子吗?我感觉您仍然可以使用CursorProvider,但您可能希望与事件混合而不是更改Provider.value

标签: javascript reactjs react-hooks next.js


【解决方案1】:

问题是您正在记忆使用上下文的部分。任何绑定到上下文的组件都将在它更改时重新渲染 - 否则它应该如何具有新的上下文状态?

您需要做的是将状态绑定推送到它使用的位置。所以在你的情况下,你需要从

const Layout = ({ children }: Props): JSX.Element => {
  const { width } = useWindowSize();
  const memoizedJSX = useMemo(() => {
    return (
      <div id="App" className="relative">
        <Head>
          <title>Virgile Hasselmann</title>
          <meta
            name="description"
            content="Virgile Hasselmann, a video and photo portfolio"
          />
          <link rel="icon" href="/favicon.ico" />
        </Head>
        {width! > 768 && <CustomCursor />}
        <header className="fixed w-screen z-50">
          <Navbar />
        </header>
        <main className="min-h-screen">
          <div>{children}</div>
        </main>
      </div>
    );
  }, []);
  return memoizedJSX;
};

const Layout = ({ children }: Props): JSX.Element => {
    return (
      <div id="App" className="relative">
        <Head>
          <title>Virgile Hasselmann</title>
          <meta
            name="description"
            content="Virgile Hasselmann, a video and photo portfolio"
          />
          <link rel="icon" href="/favicon.ico" />
        </Head>
        <CursorWithWidth/>
        <header className="fixed w-screen z-50">
          <Navbar />
        </header>
        <main className="min-h-screen">
          <div>{children}</div>
        </main>
      </div>
    );
};
const CursorWithWidth = () => {
  const { width } = useWindowSize();
  return {width! > 768 && <CustomCursor />}
}

现在当 Context 改变时,只有 CursorWithWidth 会重新渲染。如果你记下 CustomCursor - 你也会让那个孩子短路!

一般来说,你应该小心你的组件数据绑定.并尝试co-locate your data dependencies。这也可以方便地清理您的代码,使其更具可读性,并且您的组件更可重用,因为它们专注于更少的事情。 (参见Clean Code 的第 2 章)。

考虑到这一点,另一个好的做法是不要使用 useWindowSize() 之类的钩子来混淆数据绑定。相反,通过直接在组件的开头使用 useContext 挂钩来明确绑定发生的位置。当你一开始有一堆数据绑定调用时,你通常可以看到一个组件做太多事情。您真的需要将所有数据放在同一个地方吗?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-06
    • 1970-01-01
    • 1970-01-01
    • 2017-11-16
    • 2016-05-26
    • 2018-08-14
    • 1970-01-01
    相关资源
    最近更新 更多