简体   繁体   English

如何对 next.js 中的每个页面使用 getServerSideProps?

[英]How to use getServerSideProps for every pages in next.js?

I have set a cookie with nookies which store the values of all the products selected by user.我设置了一个带有 nookies 的 cookie,它存储用户选择的所有产品的值。 I want to fetch the cookie in server side using getServerSideProps and pass the value as props.我想使用 getServerSideProps 在服务器端获取 cookie 并将值作为道具传递。 I have to display the value of cookie on all pages.我必须在所有页面上显示 cookie 的值。

When I tried getServerSideProps in _app.js.当我在 _app.js 中尝试 getServerSideProps 时。 It did not worked and it did not even run the code.它没有工作,甚至没有运行代码。

Is there any way to do it?有什么办法吗?

getServerSideProps does not work in _app.js . getServerSideProps_app.js中不起作用。 see docs .文档

you could use the older getInitialProps in your custom app component but then the automatic static optimisation is disabled, which is something Next.js bets on heavily.您可以在自定义应用程序组件中使用较旧的getInitialProps ,但随后会禁用自动 static 优化,这是 Next.js 非常重视的事情。

it might be worth digging into your cookie use case and figure out if you really need to read it on the server side.可能值得深入研究您的 cookie 用例并确定您是否真的需要在服务器端读取它。

As of now, there isn't a built-in way to do it, so I've resorted to doing the following.到目前为止,还没有内置的方法可以做到这一点,所以我采取了以下措施。

First, I created a file that holds the getServerSideProps function I want to run on every page:首先,我创建了一个文件,其中包含我想在每个页面上运行的getServerSideProps function:

// lib/serverProps.js
export default async function getServerSideProps(ctx) {
  // do something
  return {
    // data
  };
}

Then in every page (yes, every , I can't find a workaround; it might even be helpful if you don't need the code to execute on server pages), do:然后在每个页面中(是的,每个页面,我都找不到解决方法;如果您不需要在服务器页面上执行代码,它甚至可能会有所帮助),请执行以下操作:

import getServerSideProps from "../lib/serverProps";

// other stuff...

export { getServerSideProps };

or或者

// other stuff...

export { default as getServerSideProps } from "../lib/serverProps";

If you want to add other code to run inside getServerSideProps for a specific page, you could do something along the lines...如果您想添加其他代码以在特定页面的getServerSideProps中运行,您可以执行以下操作...

import serverProps from "../lib/serverProps";

// other stuff...

export async function getServerSideProps(ctx) {
  // do custom page stuff...
  return {
    ...await serverProps(ctx),
    ...{
      // pretend this is what you put inside
      // the return block regularly, e.g.
      props: { junk: 347 }
    }
  };
}

I used a slightly different technique.我使用了一种稍微不同的技术。 Every page, in my case, has its own getServerSideProps and I was looking for a more functional approach.就我而言,每个页面都有自己的getServerSideProps ,我一直在寻找更实用的方法。 Also I'm using GraphQL, but the idea is the same no matter which data fetching API you choose.此外,我使用的是 GraphQL,但无论您选择哪种数据获取 API,这个想法都是一样的。 A regular getServerSideProps would look like this -常规的getServerSideProps看起来像这样 -

export const getServerSideProps: GetServerSideProps = async (context) => {

    const { slug } = context.query

    const { data: profile } = await client.query({ query: GetProfileDocument, variables: { slug } })

    return {
        props: {
            ...(await getSelf(context)),
            profile: profile?.GetProfile[0],
        },
    }
}

In the props you can see the await statement, which is called in all pages.props中可以看到await语句,在所有页面中都会调用。 And in the few cases I don't need it, it's gone.在少数情况下我不需要它,它已经消失了。 This is what getSelf looks like -这就是getSelf的样子——

const getSelf = async (context: GetServerSidePropsContext<ParsedUrlQuery, PreviewData>) => {
    const session = await getSession(context)

    let self = null

    if (session) {
        const { data } = await client.query({
            query: GetProfileDocument,
            variables: { secret: session?.secretSauce as string },
        })

        self = data.GetProfile[0]
    }

    return { self, sessionData: session }
}

Hope it helped.希望它有所帮助。

For those wanting to share state received from a page's getServerSideProps function to global components in pages/_app.tsx , I've pieced this solution together.对于那些希望将从页面的getServerSideProps function 接收到的 state 共享给pages/_app.tsx中的全局组件的人,我已经将这个解决方案拼凑在一起。

  • Create a shared getServerSideProps function to include on all pages创建一个共享的getServerSideProps function 以包含在所有页面上
  • Create a shared useSetUserStorage custom hook to include on all pages创建一个共享的useSetUserStorage自定义挂钩以包含在所有页面上
  • Listen for localStorage changes with custom event listener in global component (eg GlobalNav )使用全局组件(例如GlobalNav )中的自定义事件侦听器侦听localStorage更改

It's a work around, but is working for me so far (note that it includes some specifics to my use of getServerSideProps function).这是一种解决方法,但到目前为止对我有用(请注意,它包括我使用getServerSideProps函数的一些细节)。

It's a fair amount of code but hopefully this helps someone:这是相当多的代码,但希望这可以帮助某人:

// src/pages/_app.tsx

import type { AppProps } from "next/app";

import GlobalNav from "../components/GlobalNav";

function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
  return (
    <>
      <GlobalNav /> // <— your global component
      <Component {...pageProps} />
    </>
  );
}

export default MyApp;
// src/utils/getServerSideProps.ts

import { ppinit, ppsession, sess } from "../authMiddleware";
import nc from "next-connect";
import { NextApiRequest, NextApiResponse } from "next";

import { User } from "../types/types";

interface ExtendedReq extends NextApiRequest {
  user: User;
}

interface ServerProps {
  req: ExtendedReq;
  res: NextApiResponse;
}

interface ServerPropsReturn {
  user?: User;
}

//
// Here we use middleware to augment the `req` with the user from passport.js
// to pass to the page
// src: https://github.com/hoangvvo/next-connect/tree/21c9c73fe3746e66033fd51e2aa01d479e267ad6#runreq-res
//
const getServerSideProps = async ({ req, res }: ServerProps) => {
  // ADD YOUR CUSTOM `getServerSideProps` code here
  const middleware = nc()
    .use(sess, ppinit, ppsession)
    .get((req: Express.Request, res: NextApiResponse, next) => {
      next();
    });

  try {
    await middleware.run(req, res);
  } catch (e) {
    // handle the error
  }

  const props: ServerPropsReturn = {};
  if (req.user) props.user = req.user;
  return { props };
};

export interface Props {
  user?: User;
}

export default getServerSideProps;
// src/hooks.ts

import { useEffect } from "react";

import { User } from "./types/types";

export const useSetUserStorage = (user?: User) => {
  useEffect(() => {
    if (user) {
      localStorage.setItem("user", JSON.stringify(user));
    } else {
      localStorage.removeItem("user");
    }

    // whether setting or removing the user, dispatch event so that `GlobalNav`
    // component (which is above the page implementing this hook in the
    // component hierarchy) can be updated to display the user status.  we
    // can't use `window.addEventListener('storage', handler)` as this only
    // works for listening for events from other pages
    document.dispatchEvent(new Event("localStorageUserUpdated"));
  });

  return null;
};
// src/pages/index.tsx (or any page)

import { useSetUserStorage } from "../hooks";

import { Props } from "../utils/getServerSideProps";
export { default as getServerSideProps } from "../utils/getServerSideProps";

export default function Home({ user }: Props) {
  useSetUserStorage(user);

  return (
    <>
      <h1>Welcome to my app {user?.username}</h1>
    </>
  );
}
// src/components/GlobalNav.ts (or another global component)

import { useEffect, useState, MouseEvent } from "react";

import { User } from "../types/types";

const GlobalNav = () => {
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    const handleUserLocalStorage = () => {
      const userString = localStorage.getItem("user");

      try {
        if (userString) {
          setUser(JSON.parse(userString));
        } else {
          setUser(null);
        }
      } catch (e) {
        // handle parse error
      }
    };

    handleUserLocalStorage();
    // this component (`GlobalNav`) lives at the application level, above the
    // pages, but the pages receive the user object from `getServerSideProps`,
    // so this listener listens for when a page tells us the user object has
    // changed so we can update the `user` state here.
    document.addEventListener(
      "localStorageUserUpdated",
      handleUserLocalStorage,
      false,
    );

    return () => {
      // remove listener if component unmounts
      document.removeEventListener(
        "localStorageUserUpdated",
        handleUserLocalStorage,
      );
    };
  }, []);

  return (
    <div>
      {user?.username}
    </div>
  );
};
export default GlobalNav;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM