I have a global variable that is an array.
In 2 different components they add an element to the array simultaneously on mount. But in order to add an element to the array they need to clone the current one & add to it. Since they're mounting simultaneously, the array is empty when they clone it. So only one element is added to the array.
How can I use useEffect to run the inserted code once on mount & run the return code on dismount reading a global variable that can change.
here's an example
store.js
import { setGlobal } from 'reactn';
const initStore = () => {
setGlobal({
globalArray: [],
});
};
export default initStore;
component-number-one.js
import React,{useEffect, } from 'react';
// works just like useState but makes the variables accessible anywhere
import { useGlobal, } from 'reactn';
// Just a clone util, clones stuff
import { clone } from '../../../utils/clone';
const ComponentNumberOne = () => {
const [globalArray, setGlobalArray] = useGlobal('globalArray');
useEffect(() => {
// Only want this to run on mount
let newGlobalArray = clone(globalArray); // Problem is here, doesn't know globalArray has an updated value
newGlobalArray.push("ComponentNumberOne");
setGlobalArray(newGlobalArray);
// Only want this to run on dismount
return () => {
let newGlobalArray = clone(globalArray); // Possibly problem here too
let index = newGlobalArray.indexOf("ComponentNumberOne");
newGlobalArray.splice(index,1);
setGlobalArray(newGlobalArray);
}
},[]);
return (
<div className="ComponentNumberOne">
ComponentNumberOne stuff
</div>
);
};
export default ComponentNumberOne;
component-number-two.js
import React,{useEffect, } from 'react';
// works just like useState but makes the variables accessible anywhere
import { useGlobal, } from 'reactn';
// Just a clone util, clones stuff
import { clone } from '../../../utils/clone';
const ComponentNumberTwo = () => {
const [globalArray, setGlobalArray] = useGlobal('globalArray');
useEffect(() => {
// Only want this to run on mount
let newGlobalArray = clone(globalArray); // Problem is here, doesn't know globalArray has an updated value
newGlobalArray.push("ComponentNumberTwo");
setGlobalArray(newGlobalArray);
// Only want this to run on dismount
return () => {
let newGlobalArray = clone(globalArray); // Possibly problem here too
let index = newGlobalArray.indexOf("ComponentNumberTwo");
newGlobalArray.splice(index,1);
setGlobalArray(newGlobalArray);
}
},[]);
return (
<div className="ComponentNumberTwo">
ComponentNumberOne stuff
</div>
);
};
export default ComponentNumberTwo;
The useEffect
should have globalArray
in its dependencies array.
Of course, just putting globalArray
in the dependencies breaks your code.
The actual problem is that your code behaves "imperatively", i.e. "if the component is mounted, add an item to the array. That is not the way React is supposed to be used.
Instead of telling React when to do something, you should tell React what state you expect. I.e. update the array to represent the desired state, e.g.:
useEffect(() => {
if( !globalArray.includes("ComponentNumberTwo") ){
setGlobalArray([
...globalArray,
"ComponentNumberTwo"
]);
};
}, [ globalArray ]);