I have two useEffects in my functional component, in which useEffect 1 is executed first and then useEffect 2 is executed based on the useState value in useEffect 1 but if you see the log below, useEffect 2 is executed in the middle of useEffect 1 (which hasn't finished yet)
Is this normal behaviour or am I doing anything wrong? Help in this regard is much appreciated. I am a beginner learning MERN Stack.
I want to get the response from API call first and then only execute useEffect 2.
Thanks in advance
const Messenger = () => {
const user = useSelector((state) => state.user.currentUser);
const state = useRef(useLocation().state); // useRef to change value and persist it for every re-render
console.log(state)
const [conversations, setConversations] = useState([]);
// Get the conversations if any from DB
useEffect(() => {
console.log("useEffect 1")
console.log("get Conversations if any")
const getAllConversations = async () => {
try {
localStorage.setItem("user", user);
const res = await getConversationsAPI(user?._id)
console.log(res.data)
setConversations(res.data);
console.log("Set conversations:")
} catch (err) {
console.log(err);
}
localStorage.removeItem("user");
};
getAllConversations();
}, [user]);
useEffect(() => {
console.log("useEffect 2")
if (state?.current) {
console.log("Conversation already Exists useEffect")
for (let convo of conversations) {
console.log(convo)
if (convo?.members.some(mem => mem._id === state?.current?._id)) {
console.log("Conversation already exists..", state?.current._id)
state.current = null;
console.log(state.current)
};
};
};
}, [state, conversations]);
[Log] useEffect 1
[Log] get Conversations if any
[Log] Get Conversations API: User
[Log] Bef api: – "632d33562aafdba7835b30ac"
[Log] useEffect 2
[Log] Conversation already Exists useEffect
[Log] Aft api: – "632d33562aafdba7835b30ac"
[Log] [Object, Object] (2)
[Log] Set conversations:
[Log] useEffect 2
[Log] Conversation already Exists useEffect
[Log] {_id: "634cf4adb10c58cd8b248591", members: Array, createdAt: "2022-10-17T06:22:37.985Z", updatedAt: "2022-10-17T06:22:37.985Z", __v: 0}
[Log] Conv already exists..state – "632c208f8bb1b20a5604f210"
[Log] null
[Log] {_id: "634e3ff52834916e46595fff", members: Array, createdAt: "2022-10-18T05:56:05.990Z", updatedAt: "2022-10-18T05:56:05.990Z", __v: 0}
[Log] {current: null}
CodePudding user response:
Yes, that's normal behavior. All useEffect
s will run at least once.
I see your second effect depends on state
. If you can overcome that dependency, you could just use one useEffect
. You would run your logic that handles the result from the API in the first effect itself, just after the setConversations
.
If for some reason you do depend on state
, in your scenario, you could create a state to mark when the conversations are loaded (e.g., conversationsLoaded
) and then use that state in the second effect to make sure you only execute your logic after the conversations were loaded. Example:
const Messenger = () => {
// ...
const [conversations, setConversations] = useState([]);
const [conversationsLoaded, setConversationsLoaded] = useState(false);
useEffect(() => {
const getAllConversations = async () => {
try {
// ...
const res = await getConversationsAPI(user?._id)
setConversations(res.data);
setConversationsLoaded(true);
} catch (err) {
// ...
};
getAllConversations();
}, [user]);
useEffect(() => {
// ...
if (conversationsLoaded) {
// now you know conversations were loaded and can work with them safely
}
// ...
}, [state, conversationsLoaded, ...]);
CodePudding user response:
the second useEffect
is going to fire right away no matter what.. then after that it will wait for values to change in the dependency array. does that answer your question?
CodePudding user response:
instead of using useEffect-2
you can simply add a flag on api response and based on that flag state execute what you want.
a rough example can be like:-
on api response flag = true
and after this write a function outside useEffect
if(flag){
..whaterver you want to execute..
}
CodePudding user response:
useEffect
runs once when the component initially mounts, and runs again every time one of the dependencies passed to it changes. You can't prevent the execution of useEffect
itself, but if you want to execute the logic inside it after some conditions are met, there is a simple workaround.
When you say you want to run one useEffect
after another, you mean that you want one piece of code to produce an outcome before another runs. In your case, I assume that you want all the conversations loaded before your code inside useEffect 2 runs.
useEffect(() => {
if (conversations.length) {
// do whatever you need to in useEffect 2
}
}, [state, conversations]);