3

In my React app, I need to store some data which needs to be accessible pretty much everywhere, but don't want to keep it in a global (i.e., static) data structure so that it's easy to mock for my unit tests and the style guide.

The data I mean is for example:

  • a constant IN_BROWSER that is true in browser builds and false in Node.
  • a constant IS_MOBILE that is initialized on the startup in the browser
  • data of the user who is currently logged in,
  • url of the API that I connect to (which is localhost, staging server, or production, depending on the config)

Right now I have a file called sessionData.js that stores this data, and whenever I need, I do require('./sessionData') in my code and use it. I can mock it for my unit tests using rewire and, as they run sequentially, it works fine for now. It is problematic for the styleguide, because there each example can show the view from the perspective of a different user (so ideally each example would have its own sessionData).

IN_BROWSER is currently a global, which also turns out to be a bad idea, because it puts an implicit dependency on the initialization code from every module.

I see that I could pass my sessionData and other stuff via props to every component down the hierarchy, but this seems like a lot of redundancy.

Is there any design pattern to solve it better?

mik01aj
  • 11,928
  • 15
  • 76
  • 119

4 Answers4

1

I use Webpack and there is a plugin called DefinePlugin that handle this to me. I guess it's a good practice as it's where I place most of my app config details.

For example, my webpack config has:

new webpack.DefinePlugin({
  'process.env': {'NODE_ENV': JSON.stringify(options.env)}
}),

So I can use process.env.NODE_ENV in the front-end as in the back-end.

I open sourced my webpack config here: https://github.com/agendor/react-webpack-sample/blob/master/webpack.config.js

And here is a link for the plugin documentation

For unit tests, I use a test-helper.js class that define some global variables and I require it in the beginning of every test. I'm not sure if this is a good practice but it's working fine for our project. We run our tests without webpack. Maybe a better practice would be to have a specific webpack config for tests that handle some of these global variables.

Tulio
  • 955
  • 6
  • 12
  • This one is good, but it helps only for the 1st case, the `IN_BROWSER` constant. – mik01aj Nov 17 '15 at 13:05
  • @m01 I thought you could add another argument to the plugin that points to your `sessionData.js` module so you could use it without requiring every time. But you would have to return a function instead of a constructed object. Not sure if it's suitable. – Tulio Nov 23 '15 at 18:37
  • The problem with sessionData is that I don't want it to be global. I want to change it dynamically for each unit test or each styleguide example. – mik01aj Nov 24 '15 at 08:32
1

If I'm understanding you correctly, why not just pass down the data structure through props into all of the components that may need it? Obviously you can't modify it through props but within that data structure you can always include a callback function to update it, something with a structure like:

structure = {
  dataItem1: stuff,
  dataItem2: stuff2,
  dataItemCallback: { var foo = stuff here; }
}

You can then call this like:

this.props.structure.dataItem1;
this.props.structure.dataItem2;

To update something you can always call:

this.props.structure.dataItemCallback(newData);

Also, if you pass it through props, now all the components can rerender if anything in the structure changes.

Bruce
  • 77
  • 3
  • 9
  • It seems like a good idea, but then I worry that the structure will grow bigger and bigger (and thus harder to mock or maintain). Could you elaborate on how can you keep it simple and maintainable? – mik01aj Nov 18 '15 at 15:19
  • I'd have to say that potentially your view components may be too complicated if the view is having to worry about the structure that much. At the view level the components should just be getting props and rendering them without any logic. The view controllers may have some extra logic to handle where all those pieces of the data structure get to and just pass them down individually through props to the dumb view components. As to how to make the structure simple and maintainable? All depends on your individual needs I suppose :) – Bruce Nov 19 '15 at 12:10
0

Looks like react context is the way to go.

Unfortunately the API is currently not stable, e.g. React.withContext got deprecated in 0.13-alpha and the docs say that the API will change in the future, but it seems that the context as such isn't going to be deprecated.

mik01aj
  • 11,928
  • 15
  • 76
  • 119
0

In some cases, it's possible to refactor the components to pass the needed data directly instead of having it in a global state. This doesn't solve the problem, but can significantly reduce it. Here's a nice explanation from Dan Abramov:

enter image description here

mik01aj
  • 11,928
  • 15
  • 76
  • 119