ReactJS: Things to consider before using Closures in ReactJS 
Tags: javascript, closure
Published: 2021-05-10 10:43:39

Closures can `'break'` reactivity if misused - Let's have a look.

In JS, `closure` is a function that `"remembers"` all variables from its parent scope. 

For example, following closure will be executed after `1000ms`. And it will have access to `date` variable that is not available inside the callback passed to `setInterval`. This is how a closure `"remembers"` variables from its parent scope.

```js
var date = new Date();
setInterval(() => {
    console.log(date);
}, 1000);
```

But if this `date` variable is itself reactive & keeps on changing then `closure` will not be able to keep track of changing value of `date` variable.

I found out this behavior while I was developing a `socket based chat app`. Chat app mainly consists of functional components so I was registering `socket io event listeners` inside `useEffect()` as follows:

```js
  const [time, setTime] = useState(new Date());
  useEffect(() => {
    socket.emit("new_message", function (newMessageData) {
      console.log("time", time);
    });
  }, []);
```

here `time` is a variable that is `reactive` and its value is changed every 900ms using following `useEffect` hook:

```js
  useEffect(() => {
    /*** updates time every 1000ms ***/
    setInterval(() => {
      setTime(new Date());
    }, 1000);
  }, []);
```

After combining above code into a single component `App.js`, my code looks like this:

```js
import "./styles.css";
import React, { useState, useEffect } from "react";
/***** 
following socket object is to mimic 
socket client for the sake of example 
***/
const socket = {
  on: function (type, cb) {
    setInterval(() => {
      cb();
    }, 1000);
  }
};
/****** ***/

export default function App() {
  const [time, setTime] = useState(new Date());

  useEffect(() => {
    setInterval(() => {
      setTime(new Date());
    }, 1000);
  }, []);

  useEffect(() => {
    socket.on("new_message", function () {
      console.log("time", time);
    });
  }, []);
  return <div className="App">{time.toString()}</div>;
}
```

Problem is `new_message`, `closure` doesn't keep track of changes that are being made to `time` variable & this is because `closure` only remembers first value of `time` variable.

Have a look at this https://storyxpress.co/video/koigqbiphyw2b0v9b
value of `time` variable doesn't change in logs as this is coming from `new_message` handler through a `closure`.

However we can fix it like this:

```js
import "./styles.css";
import React, { useState, useEffect } from "react";
const socket = {
  on: function (type, cb) {
    setInterval(() => {
      let newMessageData = new Date().getTime();
      cb(newMessageData);
    }, 1000);
  }
};
export default function App() {
  const [time, setTime] = useState(new Date());

  useEffect(() => {
    setInterval(() => {
      setTime(new Date());
    }, 1000);
  }, []);

  const [newMessageData, setNewMessageData] = useState(new Date());
  useEffect(() => {
    socket.on("new_message", function (data) {
      setNewMessageData(data);
    });
  }, []);

  useEffect(() => {
    console.log("time.....", time);
  }, [newMessageData]);

  return <div className="App">{time.toString()}</div>;
}
```
Now instead of directly accessing `time` variable inside `closure` I am just setting value `newMessageData` using a hook. 

Then I attached another `useEffect()` hooks that keeps track of changes to `newMessageData` and fetches an updated `time` variable whenever `newMessageData` changes. 

Have a look at this screencast https://storyxpress.co/video/koigvz8sd5tdeeg0c : `time` variable is updated as it is logged to console every 1000ms.