1

I am making a simple React application in which there is a app.tsx file with the code as follows,

export default function App() {
  const initializeData = () => {
    const dataScript = document.createElement("script");
    dataScript.type = "text/javascript";
    dataScript.textContent = `
    let data = { 'name': "" };`;
    document.head.appendChild(dataScript);
  };

  React.useEffect(() => {
    initializeData();
  }, []);

  return (
    <div className="App">
      {console.log(data)}
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

-> Where initializeData method will intialize a global data and that will be available for all other pages (considering all components will be called from here in app.tsx ).

-> Calling the method from useEffect lifecycle hook like,

  React.useEffect(() => {
    initializeData();
  }, []);

-> Trying to console.log the data in template like,

 return (
    <div className="App">
      {console.log(data)}
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );

Error:

This line {console.log(data)} throws the error as data is not defined

Requirement:

Need to make a variable declaration in global level so those variables would be available for all files.

Keeping this in mind only I have tried assigning it in head tag inside script tag but this shows error in typescript.

Edit stupefied-newton-ixwut

Please help me on how to assign a global variable and access it inside any files..

4
  • "Need to make a variable declaration in global level so those variables would be available for all files." Using globals at all in a modern JS/TS page or app is not best practice without a very strong reason to do it, but creating a global variable when a component mounts is never a good idea. Whatever your underlying requirement is, I strongly recommend you find another way to solve it. Ideally, don't use a global at all (you really shouldn't need to in a world with modules), but if you cant avoid it, at least create it when the module is loaded, not later when the component is mounted. Commented Dec 30, 2020 at 9:07
  • @T.J.Crowder, I am using like this to handle adobe analytics for the page.. I am using npmjs.com/package/react-metrics to track the events and for that I am in the need to set a global variable and it will get changed on every page.. Like whichever page we are visiting then that page name (along with other data) will be passed down to metrics track function.. Commented Dec 30, 2020 at 9:10
  • 1
    I don't see anything on that npm page suggesting you need to use a global variable. I do see it saying you need to wrap your application component via a call to metrics which you don't seem to be doing above. (My guess is that it's doing that in order to set up a context that MetricsElements and such can use, but that's just a guess.) But I don't want to get into a back-and-forth about whether you need a global variable. Your question was about why data doesn't exist, which I've answered below. Happy coding! Commented Dec 30, 2020 at 9:15
  • 1
    Looks like an XY problem xyproblem.info, you asking about your attempt instead of tackling the real problem Commented Dec 30, 2020 at 9:22

1 Answer 1

1

Your useEffect callback doesn't run until after your attempt to use the data variable in the JSX you're returning. From the useEffect documentation:

The function passed to useEffect will run after the render is committed to the screen.

The order in which things happen when your App function is first called is:

  1. The initializeData function is created.
  2. Your useEffect callback function is created.
  3. useEffect is called with that callback and an empty array.
  4. Your JSX is evaluated, including the console.log(data) call — causing the error.

If it weren't for the error, things would continue like this:

  1. The React element (and its children) you return would be put in the DOM by React.
  2. Then React would call your useEffect callback, which would create the script element creating the global variable.

You could use typeof data === "undefined" to know that it's the first render and data may not exist yet, but you'll also need to do something else to make your component function get called again (e.g., have a state variable you update in your useEffect callback). But creating a global variable when a component mounts is, to put it mildly, not best practice and I strongly recommend you look to solve your underlying requirement in a different way.

Sign up to request clarification or add additional context in comments.

4 Comments

Even if you fix the useEffect how can you use the data variable globally without appending it to a global object?
@DennisVash - let declarations at global scope create globals, even though they don't create properties on the global object. (There are something like six layers of globals in the browser environment, at least two of which are defined by JavaScript itself.) So the OP's script they're injecting will create a global called data eventually. Just not in time for the JSX to use it the first time App is called. :-) The second call would have access to it, if one occurred.
Yea you right I checked it: codesandbox.io/s/jolly-wood-c455x?file=/src/App.tsx, nice one
If you're interested in this stuff, I -- er -- go into let and const in some detail in Chapter 2 of my recent book JavaScript: The New Toys. Links in my profile. :-D (Though I don't go into the six layers of globals. I need to do a blog post on that, it's been coming up a lot lately...)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.