This content originally appeared on Bits and Pieces - Medium and was authored by Ajay n Jain
Global Scoped Variables in useEffect Dependencies? A Big NO!
Outer scope values like ‘window.location.pathname’ aren’t valid dependencies because mutating them doesn’t re-render the component. (react-hooks/exhaustive-deps)
The use case
Let’s say we have to perform an operation on each page and we have to do this whenever the route changes.
As we have to perform the operation on each page component, let’s abstract the logic out into a hook.
Files
index.js files contains Router config with two pages -> Home Page and Other Page.
Home Page and Other Page renders just a text and calls the common useLocation hook.
In useLocation hook, we will perform some operation based on window.location.pathname which is a global scoped variable.
// FILENAME -> index.js
import React from "react";
import { render } from "react-dom";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import Other from "./components/Other";
import Home from "./components/Home";
// Basic Router to render Home Page and Other Page
const BasicExample = () => (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/other">Other</Link>
</li>
</ul>
<hr />
<Route exact path="/" component={Home} />
<Route path="/other" component={Other} />
</div>
</Router>
);
render(<BasicExample />, document.getElementById("root"));
// FILENAME -> useLocation.js
// This is the hook that we will be running on each page
import { useEffect } from "react";
const useLocation = (pathname) => {
useEffect(() => {
/*
* If the pathname is same as current page path, perform some operation
* For this example, the operation is to alert the user
* only if pathname passed and window.location.pathname are equal
*/
if (pathname === window.location.pathname) {
alert(
`On Page -> , pathname -> ${pathname},
window.location.pathname -> ${window.location.pathname}`
);
}
}, [pathname, window.location.pathname]);
};
export default useLocation;
// Home Page
import React from "react";
import useLocation from "../useLocation";
const Home = () => {
useLocation("/");
return <h2>Home</h2>;
};
export default Home;
// ----------------------------------------------------------------------
// Other Page
import React from "react";
import useLocation from "../useLocation";
const Other = () => {
useLocation("/other");
return <h2>Other</h2>;
};
export default Other;
Interaction
In the interaction, we can see that, once we land on Home Page, we see an alert of path -> ‘/’ and navigating to Other Page, we see an alert of path -> ‘/other’.
Everything seems to be working fine, so what’s the problem?
The Problem
Let’s modify the code to perform cleanup operation once the effect has finished running.
In the modified code, we will be alerting as part of the cleanup operation.
// FILENAME -> useLocation.js
// This is the hook that we will be running on each page
import { useEffect } from "react";
const useLocation = (pathname) => {
useEffect(() => {
/*
* If the pathname is same as current page path, perform some operation
* For this example, the operation is to alert the user
* only if pathname passed and window.location.pathname are equal
*/
if (pathname === window.location.pathname) {
// MODIFIED CODE HERE -> removed alert from this condition
// doSomeOperation()
}
// MODIFIED CODE HERE -> CLEANUP OPERATION
// Added the same alert here
return () => {
alert(
`On Page -> , pathname -> ${pathname},
window.location.pathname -> ${window.location.pathname}`
);
}
}, [pathname, window.location.pathname]);
};
export default useLocation;
Let’s see the interaction now
When we navigate to Other Page, the Home Page-effect has completed running and runs the cleanup operation.
While we expect both path and pathname to be same, in the alert we have pathname -> ‘/’ and window.location.pathname -> ‘/other’.
This happens because window.location.pathname is mutated by the time cleanup operation has started.
The same thing happens when we navigate back to Home Page from Other Page.
The Solution
Let’s modify the code to not use window.location.pathname in the dependency and instead, use a local variable defined in the hook.
// FILENAME -> useLocation.js
// This is the hook that we will be running on each page
import { useEffect } from "react";
const useLocation = (pathname) => {
// MODIFIED CODE HERE -> Assigned path = window.location.pathname
// Use this variable across the hook
const path = window.location.pathname;
useEffect(() => {
/*
* If the pathname is same as current page path, perform some operation
* For this example, the operation is to alert the user
* only if pathname passed and path are equal
*/
if (pathname === path) {
// doSomeOperation()
}
return () => {
alert(
`On Page -> , pathname -> ${pathname},
window.location.pathname -> ${path}` // Use path instead of window.location.pathname
);
}
}, [pathname, path]);
};
export default useLocation;
Note: It might be a good idea to extract this code into a custom hook called useLocation. You can then use an open-source toolchain like Bit to publish, version, and reuse it across all of your projects with a simple npm i @bit/your-username/use-location. Find out more here, and here. Anyway, moving on.
Let’s see the interaction now
After applying the fix, we can see that we get correct and consistent values.
This works because path is a local variable of each hook instance. While it is assigned to window.location.pathname at the init, the reassignment only happens when there is a rerender.
Have you ever faced issues like these! Do let me know in the comments!
Other stories by me
Build Apps with reusable components, just like Lego
Bit’s open-source tool help 250,000+ devs to build apps with components.
Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.
Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:
→ Micro-Frontends
→ Design System
→ Code-Sharing and reuse
→ Monorepo
Learn more
- How We Build Micro Frontends
- How we Build a Component Design System
- How to reuse React components across your projects
- 5 Ways to Build a React Monorepo
- How to Create a Composable React App with Bit
Global Scoped Variables in useEffect Dependencies? A big NO! was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Bits and Pieces - Medium and was authored by Ajay n Jain
Ajay n Jain | Sciencx (2023-02-16T09:41:33+00:00) Global Scoped Variables in useEffect Dependencies? A big NO!. Retrieved from https://www.scien.cx/2023/02/16/global-scoped-variables-in-useeffect-dependencies-a-big-no/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.