Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: Global state at the render/hydrate level #11809

Closed
johnnysprinkles opened this issue Dec 8, 2017 · 7 comments
Closed

Feature request: Global state at the render/hydrate level #11809

johnnysprinkles opened this issue Dec 8, 2017 · 7 comments

Comments

@johnnysprinkles
Copy link

johnnysprinkles commented Dec 8, 2017

This is a feature request. It relates to https://discuss.reactjs.org/t/suggestion-for-global-context/9035

This has been a pain point for me, and I'm sure we can do better. The issue: How to have some state available to all components without passing everything down on props through the component tree.

I'm speaking about global state. So why not use context? I have a couple of issue with it.

  1. It's not the root component's job to receive and disseminate state. The root component is just some component. It shouldn't care if it's root or not. Maybe you have (example using Express):

    res.render('homepage.jsx', {});

But some other page might just as well have:

res.render('otherpage.jsx', {}) where otherpage has:

<div>
  <Something />
  <Homepage />
</div>

Components can be composed however you like, they shouldn't care what depth they're at.

  1. Above is talking about a multipage app. The thinking around React seems to be so skewed toward to the special case of Single Page App. SPA is sometimes the right choice. If you're Google Maps it's clearly right. If you're something broader in scope like Amazon.com it's clearly not. The general case is much more interesting to solve.

  2. Having every root component wrapped in some <Provider> that publishes context downwards, and every non-root component wrapped in some "receiver" higher level component that declares context is just boilerplate. It's not expressive, it's bookkeeping.

  3. Alternatives: There aren't any as far as I know. Each component only knows about the props (and possibly context) passed in, it doesn't have any handle on data for the current render() call. Simply importing global state through CommonJS or ES6 modules is impossible on the serverside for anything request scoped (like query params, route params, cookies, headers, anything generated by middleware based on these things).

-- PROPOSAL --

Why not just handle global state at a higher level than props passed component to component? Why not extend ReactDOM.render to take a 4th argument for global context? It would just make everyone's life easier. Don't even worry about changes firing componentWillReceiveProps, at least as a first version.

Think of it as analogous to Express and the "req" object that's passed around. One should always have access to some "per request" object. So many things would be useful to put in there (authenticated user, geolocation, "store" from Redux, query and route params, etc, etc).

It would look like:

ReactDOM.render(rootComponent, someDiv, callback, {... request specific data ...})

Where request specific data might have things that are truly request specific and also things that are the same across requests. The latter can be done with imports but it's a bit messy -- you end up with code like:

if (typeof window != 'undefined') {
  // Client, get value from window object
} else {
  // Server, get value from filesystem or wherever
}

Just having global data available to all components in the tree simplifies things a lot. Don't you think?

@johnnysprinkles
Copy link
Author

johnnysprinkles commented Dec 8, 2017

I should add... one thing that has occurred to me is, just use a global variable like:

// For each response...
globalState = {...};
let html = ReactDOMServer.renderToString(element);
globalState = null;

Where the components can just read the global variable "globalState". This does seem hacky though, and pretty sure it won't work with streaming.

@koutron
Copy link

koutron commented Dec 13, 2017

hey @johnnysprinkles have you looked into Redux? it can be connected into React and provides a 'store' of variables that are accessible to components of your choosing.

@swyxio
Copy link
Contributor

swyxio commented Dec 13, 2017

pretty sure he's looked into redux. one common thread that this has is with the new context RFC: reactjs/rfcs#2

@johnnysprinkles
Copy link
Author

Yeah, adding Redux has the same issue... someone already using Redux though, sure you could put global state in there.

I'll reformulate this as an RFC in the new style of feature requests.

@milesj
Copy link
Contributor

milesj commented Dec 16, 2017

Why does this need to be part of React? Just put shared state in some module and import that in the files that need it?

Contrived example.

// globals.js
export default class StateMachine {
  static css = {
    blueColor: "#3d98d2"
    darkBlueColor: "#225e87"
    darkGreenColor: "#009770"
    
  };
}

// Component.js
import StateMachine from '../globals';

StateMachine.css // { ... }

Modules are evaluated and cached on first load. Not everything needs to be complicated or part of some library/framework.

@johnnysprinkles
Copy link
Author

Totally, for things non request specific that works fine. The issue is request specific data, how to share among all components? Things like "is user authenticated," "what are the current route params," etc. No way I'm aware of to do that on the server-side, aside from the current context api.

@gaearon
Copy link
Collaborator

gaearon commented Jan 6, 2018

Please create an RFC:
https://github.com/reactjs/rfcs

Otherwise it’s hard to progress on an issue like this.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants