snowape
snowape

Reputation: 1404

How to use a global variable in several files

I was trying to use a global variable in a React app for debugging purposes, and ran into some issues that made me realize that I don't understand how global variables in javascript works.

global.js:

export let myGlobal = 0

IncButton.js:

import { Component } from 'react'
import { myGlobal } from './global'

class IncButton extends Component {
    render() {
        return (
            <button onClick={() => { myGlobal = myGlobal + 1 }}>increase</button>
        )
    }
}

export default IncButton

window.inc = () => {
    myGlobal = myGlobal + 1
}

App.js:

import { myGlobal } from './global'
import IncButton from './IncButton'

function App() {
  return (
    <>
      {myGlobal}
      <IncButton />
    </>
  );
}
export default App;

Pressing IncButton throws ReferenceError: assignment to undeclared variable myGlobal. Calling inc() directly from console throws the same error.

Using let myGlobal = 0 at top of IncButton.js, and removing import removes errors, but of course this makes App.js not see the same myGlobal. My thinking is that imported variables and variables outside any function or {} should be in the same scope.

So I would like to know: Why is the above not working, what is the right way to share a global variable across files, and do React add any further complications to this?

Note 1: The rest of the code for the example above is just the default from create-react-app.

Note 2: I do know that using global variables is often not a good idea.

Upvotes: 3

Views: 4520

Answers (2)

glinda93
glinda93

Reputation: 8459

In raw javascript project, you can export a global variable in a file, and import anywhere in other files. That's what you did.

But in React, if you want to use a global state, you should use context API. Your code does not run because myGlobal is not declared as a state. Thus, React cannot track its state.

The correct method is like this:

const {useState, useContext} = React;

// you can create context outside of React component
const CounterContext = React.createContext(0);
// export default CounterContext;


// import CounterContext from 'global/counter-context';
const ChildComponent = () => {

   const counter = useContext(CounterContext);
   
   return (
     <div className="other-component">
     <p>This is Child Component that consumes CounterContext value </p>
     <p>Current counter is: {counter}</p>
     </div>
   );

};

// import CounterContext from 'global/counter-context';
const App = () => {
  const [counter, setCounter] = useState(0);
  return (
      <div className="app">
      <CounterContext.Provider value={counter}>
         <p>This is Context Provider component. Global state is maintained here and you can consume the state value in any other child components</p>
         <label>Click this button to increment global counter state: </label>
         <button onClick={() => setCounter((oldCounter) => (oldCounter + 1))}>
           Increment
         </button>
         <ChildComponent />
      </CounterContext.Provider>
      </div>
    );
};

class Root extends React.Component {
  render() {
    return (<App />);
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
.app {
  border: 1px solid red;
  padding: 5px;
  margin: 5px;
}

button {
  margin-bottom: 5px;
  margin-left: 5px;
}

.other-component {
  border: 1px solid blue;
  padding: 5px;
  margin: 5px;
}
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<div id="root">
</div>

Hooks API Reference

Upvotes: 1

Daniel
Daniel

Reputation: 6481

Quite a lot of clarifications are required to explain what happens in your code but i'll try to be succinct.

Having it your way

global.js

Is not really global but a module, and exported members are read-only.

You could make it work by exporting an object:

export let myGlobal = { count: 0 };

And changing your onClick handler to

() => { myGlobal.count++ }

But your components won't re-render, since it's not part of the state, and therefore the change won't be noticed by React.

The React way

If you want to share state between components - you should either lift the state up or use the context API as described in this answer

Upvotes: 2

Related Questions