Clearer handling of application initialization #8969
Unanswered
atombender
asked this question in
Help
Replies: 2 comments
-
This is incorrect, Next.js only calls |
Beta Was this translation helpful? Give feedback.
0 replies
-
@atombender Ever find a way to do this? |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Feature request
Is your feature request related to a problem? Please describe.
There are numerous examples of how to "bootstrap" an app with state: Redux, Apollo, authentication, and so on.
These typically rely on "augmenting" the app with various HOC wrappers. For example,
next-redux-wrapper
provides a wrapper that injects itself intoApp
, whereasexamples/with-apollo-auth
provides a wrapper that must be wrapped around every single page.They all have various solutions for the same thing: Initializing global state, storing it in the context, handling hydration/dehyration, plus using heuristics like
typeof window !== 'undefined'
to conditionally store the state in locally or in a global variable (e.g. for the Apollo client or Redux store) for when it is used client-side.It's messy.
My use case: I am using Apollo, and I want Apollo to be available both as context (so I can use components like
Query
, anduseApolloClient()
), and I need it in every page'sgetInitialProps
because that's where the translation of query parameters to actual data should be done:I also need to load some global data such as the user's account, which belongs in
App
. I also have some global data such as the current URL, extracted fromctx.req
, that I need to share as context. (The current URL also needs to be used to initialize Apollo, coincidentally.)The question is where to initialize it.
I don't like explicitly wrapping every page component (as in
examples/with-apollo-auth
) for various reasons: It's a violation of DRY; it adds an unnecessary implementation detail to every page; it adds initialization logic at the page level instead of at the application level; and it's something that needs to be done identically in every single page file (makes it easy to forget).Initially, I followed the
next-redux-wrapper
method and tried replacingApp
itself with one that loads the Apollo client (export withApollo(App)
). However, the page'sgetInitialProps
runs beforeApp
, so it does not get access to the Apollo client!So I tried the
with-apollo-auth
method and wrapped each page instead (export withApollo(MyPage)
). But this has the issue that Apollo only becomes available inside the page.App
does not ever get access to the Apollo context. That meansApp
can no longer be used as a place to insert global components that require Apollo. They all have to be pushed down to the page level.From my perspective, Next.js has a big gap in functionality here. There's no single point where this sort of initialization can be handled for both
App
and individual pages. There's also this implicit contract that each page needs to handle the transition from server-side to client-side, when Next.js itself should be the one driving this.In other words, "app-level" concerns (as opposed to page/component-level) have no explicit flow.
App
seems a bit misnamed; it's initialized after the page, but at the same time rendered around the page; it doesn't seem to represent the app as much as the outer container. (This is exemplified by the fact thatApp
in fact hasgetInitialProps
called on every router navigation, meaning that that method has to be lightweight and must be careful to not, say, load the user's account from the database on every call.) It's where one should apparently place global concerns, but not all global concerns can be placed there.I don't see what a clean solution might be for my use case, with today's Next.js, other than wrapping both individual pages and
App
. Maybe I'm missing something. But this stuff shouldn't be so hard. I shouldn't have to write complicated HOCs just to initialize some data on startup. It shouldn't be necessary to wrap pages.Describe the solution you'd like
There should be a single place to initialize the application as a whole. It should execute before the app is mounted as a React app, allowing one to write logic that can inspect
req
and the application context in a single location before a page is mounted, both server- and client-side.If initialization was made more explicit, it would also be a way to allow a "middleware" system. Look at all the permutations of
with-apollo-*
orwith-redux-*
examples. Look atwith-apollo-and-redux-saga/lib/withApollo.js
. Why isn't this just a middleware that inserts itself into the initialization stack and gives you an Apollo client and a Redux store?Beta Was this translation helpful? Give feedback.
All reactions