【问题标题】:Nextjs and Context APINextjs 和上下文 API
【发布时间】:2019-06-05 06:17:03
【问题描述】:

使用 Next.js,我试图在 getInitialProps 中获取数据后将数据保存在 Context API 状态中,以修复道具钻孔。

但是由于getInitialProps是一个静态方法,我们不能通过this.context访问它。我设法将它们保存在 componentDidMount 中,但在这种情况下,在第一页加载时,上下文状态是空的,直到它填充为止。不确定在这种情况下最佳实践是什么。我应该在哪个生命周期中将初始数据保存到 Context 以便像传递道具一样立即拥有它们?

【问题讨论】:

  • 这个问题你解决了吗?
  • 不记得了,但我不这么认为。我搬到了redux。或者,如果您可以在数据准备好之前进行加载,那就更好了。
  • 我现在用ContextApi 解决了这个问题。我将发布一个示例。

标签: reactjs next.js react-context


【解决方案1】:

您不能在 Next.js 服务器端 (SSR) 中使用 ContextAPI,因为它违反了钩子规则。 https://reactjs.org/warnings/invalid-hook-call-warning.html

React 将首先运行 getInitialProps,因此最好的解决方案是在其中获取数据并使用 ContextAPI 将其传递给您的组件。

让我们继续看看它的工作原理如下:

创建您的 AppProvider 组件

实现您希望通过 React 组件传递的上下文提供程序函数。

对于这种情况,我们将创建将整个应用程序包装在其中的全局上下文提供程序。

const AppProvider = ({ children }) => {
  const [galleryData, setGalleryData] = React.useState([]);

  const handleGalleryData = galleryData => {
    setGalleryData(galleryData);
  }

  const contextProps = {
    galleryData,
    handleGalleryData
  };

  return (
    <AppContext.Provider value={contextProps}>
      {children}
    </AppContext.Provider>
  );
}

然后用这个新的提供者包装你的应用程序。

<AppProvider>
  <App />
</AppProvider>

进入你的页面,比如index.js,试试这个方法:

Index.getInitialProps = async (props) => {
  const { req, res, query, ...others } = props;

  // use your env variables, endpoint URIs
  // ..

  ... fetch whatever you want..
  const galleryProps = await fetch(endpoint); // isomorphic-unfetch

  return {
    galleryProps,
    query,
    ...others
  };
}

根据您的 Next.js 版本,您可能会使用 getServerSideProps 而不是 getInitialProps,但请注意每个请求都会调用它。

Next.js 将使用 getServerSideProps Data Fetching docs 返回的数据在每个请求上预渲染此页面

开始在您的组件上使用 ContextAPI

然后在您的组件中,您可以检查此数据并将其存储到 ContextAPI 中

const Index = props => {
  const { galleryProps, query, ...others } = props;
  const [galleryData, setGalleryData] = useState(galleryProps);
  const { handleGalleryData, ...contextRest } = useContext(AppContext);
  ...

  // Here you're going to store data into ContextAPI appropriatly.
  useEffect(() => {
    if (typeof galleryProps === 'object' && _.keys(galleryProps).length > 0) {
      handleGalleryData(galleryProps);
    }
  }, [handleGalleryData]);

  // Other times your page is loaded, you will GET this data from ContextAPI, instead of SSR props.
  useEffect(() => {
    if (_.keys(galleryDataProps).length <= 0 && _.keys(contextRest.galleryData).length > 0) {
      setGalleryData(contextRest.galleryData);
    }
  }, []);

....

return (
  <div>
    {JSON.stringify(galleryData)}
  </div>
);

上面的用例不是最好的,但它让我们了解了如何在 Next.js 应用程序中使用 ContextAPI。我将在下面解释它:

  • 第一个useEffect() 正在验证组件是否从通过 ContextAPI 存储它的 props 接收到数据对象。

  • 第二个检查商店是否有一些数据

您可以在加载组件之前以 SSR 模式通过getInitialProps 获取数据。

参考

【讨论】:

  • 这是我目前正在寻找的一个很好的例子,所以你能在代码框(或任何其他)链接中实时给出上述实现的例子吗?这将是有帮助的。我也有怀疑是在nextjs 9中实现redux还是context api来管理状态,请问你能解释一下吗??
  • 当然可以,但我正在尝试更好的方法,所以我可以覆盖我页面中的 serverRuntimeConfig
  • 我从 next.js 开始,所以我希望从一个好的项目结构开始并管理状态。所以我希望继续使用您提供的示例链接,所以请给我用很好的例子更新解决方案..提前感谢..
  • 我刚刚更新了我的解决方案,您可以查看一下。它比 React 文档简单。试一试。有一个好的代码! :))
  • @GMaiolo,方法相同,但每次请求都会调用getServerSideProps
【解决方案2】:

当您谈论 getInitialProps 时,您正在谈论服务器端渲染 (SSR)。如果你不需要做 SSR,Next with Context API 中的示例就足够了,否则你可以使用文件_app.js 中的pageProps 来初始化你的 Context,在文档Custom App 中阅读更多关于 Custom App 的信息

注意:如果您使用的是 Next.js 9.3 或更高版本,我们建议您使用 getStaticProps 或 getServerSideProps 而不是 getInitialProps。

import { AppProvider } from '../contexts/AppProvider';

function MyApp({ Component, pageProps }) {
  return (
    <AppProvider initialData={pageProps?.initialData}>
      <Component {...pageProps} />
    </AppProvider>
  );
}

export default MyApp;

然后你就可以用服务端获取的数据来初始化你的Context了。

import { useState, createContext, useMemo } from 'react';

export const AppContext = createContext();

export const AppProvider = ({ children, initialData }) => {
  const [data, setData] = useState(initialData);

  const value = useMemo(() => ({ data, setData }), [data]);

  return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
};

但是等等!!,我们如何从页面的服务器获取数据?这可能有点混乱,但是从带有getServerSideProps 的Page 获得的pageProps 总是通过MyApp 传递到子组件。

getServerSideProps (in Page) ===&gt; MyApp({ Component, pageProps }) ===&gt; Page({pageProps})

这就是 Page 使用上下文的样子。第一次,Server 渲染页面并初始化 Context,然后可以再次获取数据或更新 Context。


import { useContext } from 'react';
import { AppContext } from '../contexts/AppProvider';

export default function Index() {
  const { data, setData } = useContext(AppContext);

  const handleOnClick = () => {
    setData(`Data from client: ${Date.now()}`);
  };

  console.log(data);

  return (
    <div>
      <div>{JSON.stringify(data)}</div>
      <button onClick={handleOnClick}>Update Context</button>
    </div>
  );
}

export function getServerSideProps() {
  const data = `Data from server: ${Date.now()}`;

  return {
    props: {
      initialData: data,
    },
  };
}

您可以检查console.log(data); 是否显示在服务器和客户端控制台上,但只能显示在客户端上。

You can view the example online here

【讨论】:

    猜你喜欢
    • 2022-10-23
    • 2022-07-08
    • 2021-02-27
    • 2020-08-19
    • 2021-08-18
    • 1970-01-01
    • 1970-01-01
    • 2022-11-22
    • 2019-06-09
    相关资源
    最近更新 更多