0

This page is the most relevant information I can find but it isn't enough.

I have a generic component that displays an appbar for my site. This appbar displays a user avatar that comes from a separate API which I store in the users session. My problem is that anytime I change pages through next/link the avatar disappears unless I implement getServerSideProps on every single page of my application to access the session which seems wasteful.

I have found that I can implement getInitialProps in _app.js like so to gather information

MyApp.getInitialProps = async ({ Component, ctx }) => {
    await applySession(ctx.req, ctx.res);
    if(!ctx.req.session.hasOwnProperty('user')) {
        return {
            user: {
                avatar: null,
                username: null
            }
        }
    }

    let pageProps = {}
    if (Component.getInitialProps) {
        pageProps = await Component.getInitialProps(ctx);
    }
    return {
        user: {
            avatar: `https://cdn.discordapp.com/avatars/${ctx.req.session.user.id}/${ctx.req.session.user.avatar}`,
            username: ctx.req.session.user.username
        },
        pageProps
    }
}

I think what's happening is this is being called client side on page changes where the session of course doesn't exist which results in nothing being sent to props and the avatar not being displayed. I thought that maybe I could solve this with local storage if I can differentiate when this is being called on the server or client side but I want to know if there are more elegant solutions.

bluewave41
  • 135
  • 2
  • 13

3 Answers3

2

I managed to solve this by creating a state in my _app.js and then setting the state in a useEffect like this

function MyApp({ Component, pageProps, user }) {
    const [userInfo, setUserInfo] = React.useState({});
    React.useEffect(() => {
        if(user.avatar) {
            setUserInfo(user);
        }
    });
    return (
        <ThemeProvider theme={theme}>
            <CssBaseline />
            <NavDrawer user={userInfo} />
            <Component {...pageProps} />
        </ThemeProvider>
    );
}

Now the user variable is only set once and it's sent to my NavDrawer bar on page changes as well.

bluewave41
  • 135
  • 2
  • 13
0

My solution for this using getServerSideProps() in _app.tsx:

// _app.tsx:

export type AppContextType = {
  navigation: NavigationParentCollection
}

export const AppContext = createContext<AppContextType>(null)

function App({ Component, pageProps, navigation }) {
  const appData = { navigation }
  return (
    <>
      <AppContext.Provider value={appData}>
        <Layout>
          <Component {...pageProps} />
        </Layout>
      </AppContext.Provider>
    </>
  )
}

App.getInitialProps = async function () {
  // Fetch the data and pass it into the App
  return {
    navigation: await getNavigation()
  }
}

export default App

Then anywhere inside the app:

const { navigation } = useContext(AppContext)

To learn more about useContext check out the React docs here.

thomallen
  • 1,926
  • 1
  • 18
  • 32
0

Other current answers kind of prove what went wrong with React. It used to be simple.

Anyway use context is an overkill, just fetch and pass as props thats why props are there.

In most cases you dont want to pass entire Objects down the line, but neatly organized presentational props.

// _app.tsx:

function App({ Component, pageProps, navigation }) {
  const appData = // fetch whatever way you like, useEffect SWR etc.
  return (
        <div>
          <Component {...pageProps} appData={appData} />
        </div>
  )
}


export default App
Tosh
  • 1,789
  • 15
  • 20