Global variables in React

Viewed 8342

I know Redux solves this but I came up with an idea.

Imagine I have an app that gets some JSON on start. Based on this JSON I'm setting up the environment, so let's assume the app starts and it downloads an array of list items.

Of course as I'm not using Redux (the app itself is quite simple and Redux feels like a huge overkill here) if I want to use these list items outside of my component I have to pass them down as props and then pass them as props again as deep as I want to use them.

Why can't I do something like this:

fetch(listItems)
  .then(response => response.json())
  .then(json => {
    window.consts = json.list;

This way I can access my list anywhere in my app and even outside of React. Is it considered an anti-pattern? Of course the list items WON'T be changed EVER, so there is no interaction or change of state.

3 Answers

What I usually do when I have some static (but requested via API) data is a little service that acts kind like a global but is under a regular import:

// get-timezones.js
import { get } from '../services/request'

let fetching = false
let timez = null
export default () => {
  // if we already got timezones, return it
  if (timez) {
    return new Promise((resolve) => resolve(timez))
  }

  // if we already fired a request, return its promise
  if (fetching) {
    return fetching
  }

  // first run, return request promise
  // and populate timezones for caching
  fetching = get('timezones').then((data) => {
    timez = data
    return timez
  })
  return fetching
}

And then in the view react component:

// some-view.js
getTimezones().then((timezones) => {
  this.setState({ timezones })
})

This works in a way it will always return a promise but the first time it is called it will do the request to the API and get the data. Subsequent requests will use a cached variable (kinda like a global).

Your approach may have a few issues:

  • If react renders before this window.consts is populated you won't be able to access it, react won't know it should re-render.
  • You seem to be doing this request even when the data won't be used.

The only downside of my approach is setting state asynchronously, it may lead to errors if the component is not mounted anymore.

From the React point of view:

You can pass the list from top level via Context and you can see docs here.

Sample of using it is simple and exists in many libraries, such as Material UI components using it to inject theme across all components.

From engineering concept of everything is a trade of:

If you feel that it's gonna take so much time, and you are not going to change it ever, so keep it simple, set it to window and document it. (For your self to not forget it and letting other people know why you did this.)

If you're absolutely certain they won't ever change, I think it's quite ok to store them in a global, especially if you need to access the data outside of React. You may want to use a different name, maybe something like "appNameConfig"..

Otherwise, React has a feature called Context, which can also be used for "deep provision" - Reference

Related