Use singletons instead of `global` and `window` in the Redwood framework

As the title reads, the proposal is to avoid (or outright ban) usage of global and window, and use singletons exported from the relevant packages for the users of code.

This could help handling access issues with server, browser, and later, other sides sides.

@danny tagged, because you and I have worked on a similar thing before.

Are there any potential drawbacks to this proposal?

If I can’t use window, how would I do something like window.print()?

The proposal is for the ban of using these globals in the redwood project itself, not in it’s users.

If redwood wants to be a bit more removed from the environment it’s used in (currently browser), then it can’t rely on the same things as the target environment.

In that case, if redwood ever had the need to use window.print(), it would ideally have a core singleton to handle redwood.print(), and a @redwood/web package, that registers window.print() in core.

Edited title to reflect this.

Got it, and makes sense :slight_smile:

I think we’ve generally been referring to them as “Redwood framework” and “Redwood apps”. I edited your title again ^^

When my wife asks what I’m doing I say “Redwood, as in building the car” or “Redwood, as in driving the car”, for framework vs app :oncoming_automobile:

2 Likes

@Tobbe like the car analogy, and @Krisztiaan like your proposal even more. Since I was not sure whether the singleton construct already exists in Javascript, I fetched this article for someone else who might think the same

Great to see you get involved in this Krisz.

My 2p on this - I don’t think there’s much value in introducing these singletons (or getter functions) within the framework, because it introduces a bit more code complexity. I do think better lint/types to prevent the use of global.whatever without a check would be sufficient.

As a user of redwood however, there maybe a benefit in introducing hooks like useWindow, useNavigator, useDocument (not sure of how deep we want to go) and warning in the use of window/document/etc. unless wrapped in useIsBrowser, useEffect or similar. The main reason for this is that it prevents accidentally breaking your code for SSR but also makes your code cleaner without needing if(isBrowser) checks everywhere.

My opinion here is very loosely held, so happy to be convinced otherwise.

I’ll pivot a bit, since currently it’s not a big deal in redwood

There are a couple issues with global.

  • they have no context, all is well in global land (auth hook, api proxy path, SSG flag all on same namespace)
  • easy to loose track of where a global is really set
  • easy to override from anywhere, with no easy way to debug resulting issues
  • there is no straightforward way to control their usage (get, set), pushing 3rd parties to try and tinker with them, or start relying on them, instead of redwood getting prompted to provide hatches and clear apis for plugin authors

A few of these is maybe, a little bit starting to show, but going further down this path, it’s way too easy to just keep following the already existing pattern.

Also, some other problems I have not highlighted, since Typescripts actually makes using globals a maybe reasonable thing to do.