Based on @ford04 comments, I could use a conditional dependency on useEffect
It however goes at the cost of having a warning
** In this example it doesn't feel logical to execute always the hook without an access being granted, but its only use is to show that a conditional dependency in useEffect is possible (which I didn't know)
import React, { useEffect, useState, createContext, useContext } from "react";
import "./styles.css";
const AuthContext = createContext(null);
const useAuth = () => useContext(AuthContext);
const useAuthProvider = () => {
const [access, setAccess] = useState(0);
return { access, setAccess };
};
const useExample = withoutAuth => {
const auth = useAuth();
const [content, setContent] = useState("");
useEffect(() => {
console.log(auth);
console.log(!withoutAuth);
if (auth && !auth.access && !withoutAuth) return;
setContent("fetched content");
console.log("done"); // this is executed always, regardless of auth
// even uselesser code
}, [withoutAuth ? null : auth]);
return [content];
};
const Button = () => {
const auth = useAuth();
return (
<button
onClick={() => {
auth.setAccess(1);
}}
>
Fetch
</button>
);
};
const Content = () => {
const auth = useAuth();
const [content] = useExample(1); // here I say NOT to implement auth
return <h2>{(auth && auth.access && content) || "restricted"}</h2>;
};
export default function App() {
const authProvider = useAuthProvider();
return (
<AuthContext.Provider value={authProvider}>
<div className="App">
<h1>Hello CodeSandbox</h1>
<Button />
<Content />
</div>
</AuthContext.Provider>
);
}
https://codesandbox.io/s/competent-currying-jcenj?file=/src/App.js
Answer from GWorking on Stack OverflowVideos
Why is the dependency array important in useEffect?
Can you use objects or arrays in the useEffect dependency array?
What happens if you donโt pass a dependency array to useEffect?
Okay I read about this somewhere... I wish I could give credit where it's due.
But you build a conditional outside your useEffect... for cleanliness... and then put that in the useEffect dep array.
See attached image.
I am finding my useEffect runs regardless of the conditional... which maybe I need to state in the dependency array... conditional === true...
Dang as I type this... that is probably my problem. But I'll lean it here incase anybody else has not see something like this before.
https://www.instagram.com/p/Cb04lovOFOV/
Sorry for the instagram drop.. but apparently the reddit react doesn't let images to be uploaded. And I'm too lazy to open VS code and copy the code again. Sorry!
Is this ok? Specifically using totalUniqueItems === 0 as a useEffect dependency?
useEffect(() => {
setFinanceOptions({
depositPercent: 10,
depositAmount: 0,
totalFinanceCost: 0,
financeCostPerMonth: 0,
interest: 17.9,
term: 36,
items: [],
}); }, [totalUniqueItems === 0, setFinanceOptions]);You can pass JSON.stringify(outcomes) as the dependency list:
Read more here
useEffect(() => {
console.log(outcomes)
}, [JSON.stringify(outcomes)])
Using JSON.stringify() or any deep comparison methods may be inefficient, if you know ahead the shape of the object, you can write your own effect hook that triggers the callback based on the result of your custom equality function.
useEffect works by checking if each value in the dependency array is the same instance with the one in the previous render and executes the callback if one of them is not. So we just need to keep the instance of the data we're interested in using useRef and only assign a new one if the custom equality check return false to trigger the effect.
function arrayEqual(a1: any[], a2: any[]) {
if (a1.length !== a2.length) return false;
for (let i = 0; i < a1.length; i++) {
if (a1[i] !== a2[i]) {
return false;
}
}
return true;
}
type MaybeCleanUpFn = void | (() => void);
function useNumberArrayEffect(cb: () => MaybeCleanUpFn, deps: number[]) {
const ref = useRef<number[]>(deps);
if (!arrayEqual(deps, ref.current)) {
ref.current = deps;
}
useEffect(cb, [ref.current]);
}
Usage
function Child({ arr }: { arr: number[] }) {
useNumberArrayEffect(() => {
console.log("run effect", JSON.stringify(arr));
}, arr);
return <pre>{JSON.stringify(arr)}</pre>;
}
Taking one step further, we can also reuse the hook by creating an effect hook that accepts a custom equality function.
type MaybeCleanUpFn = void | (() => void);
type EqualityFn = (a: DependencyList, b: DependencyList) => boolean;
function useCustomEffect(
cb: () => MaybeCleanUpFn,
deps: DependencyList,
equal?: EqualityFn
) {
const ref = useRef<DependencyList>(deps);
if (!equal || !equal(deps, ref.current)) {
ref.current = deps;
}
useEffect(cb, [ref.current]);
}
Usage
useCustomEffect(
() => {
console.log("run custom effect", JSON.stringify(arr));
},
[arr],
(a, b) => arrayEqual(a[0], b[0])
);
Live Demo
When do you need to use it? Why do I need to put a empty array even though its empty? When would I put something in the empty array?
Sorry if stupid noob question.