Is it possible to put in order React useEffects? - javascript

In 2nd. useEffect I want to return experiences using resume.id which is coming from 1st useEffect. But I guess they are working in the same time so before returns resume in 1st useEffect, 2nd one is working and it returns undefined. How can I fix this or is there a any way to write better code ?
const { authUser } = useSelector((state) => state.auth);
const [resume, setResume] = useState({});
const [experience, setExperience] = useState([]);
let resumeService = new ResumeService();
let resumeExperienceService = new ResumeExperienceService();
useEffect(() => {
resumeService
.getResumeByCandidateId(authUser.id)
.then((result) => setResume(result.data.data[0]));
}, []);
useEffect(() => {
resumeExperienceService
.getExperiencesByResumeId(resume.id)
.then((result) => setExperience(result.data.data));
}, []);

You should add the state that named 'resume' to the useEffect.
That means second useEffect would run after every setResume call
const { authUser } = useSelector((state) => state.auth);
const [resume, setResume] = useState({});
const [experience, setExperience] = useState([]);
let resumeService = new ResumeService();
let resumeExperienceService = new ResumeExperienceService();
useEffect(() => {
resumeService
.getResumeByCandidateId(authUser.id)
.then((result) => setResume(result.data.data[0]));
}, []);
useEffect(() => {
resumeExperienceService
.getExperiencesByResumeId(resume.id)
.then((result) => setExperience(result.data.data));
}, [resume]);

Related

I want to be able to delete an object from the api and re render the function without having to manually refresh the page, how can I do that?

const Notes = () => {
const history = useNavigate();
const [apiData, setApiData] = useState([]);
useEffect(() => {
axios
.get(`https://6390acc765ff4183111b53e9.mockapi.io/notes`)
.then((getData) => {
setApiData(getData.data);
});
}, []);
const onDelete = (id) => {
axios
.delete(`https://6390acc765ff4183111b53e9.mockapi.io/notes/${id}`)
.then(() => {
history("/notes");
});
};
This way I can delete the note that i fetched earlier, but it still appears on the screen until I refresh manually. It doesn't also go to /notes because i am already on /notes
You can either return the updated data in the delete response to update the local state, or you can trigger a refetch of the data after a successful deletion.
Refetch Example:
const Notes = () => {
const history = useNavigate();
const [apiData, setApiData] = useState([]);
const fetchNotes = useCallback(async () => {
const getData = await axios
.get(`https://6390acc765ff4183111b53e9.mockapi.io/notes`);
setApiData(getData.data);
}, []);
useEffect(() => {
fetchNotes();
}, [fetchNotes]);
const onDelete = async (id) => {
await axios
.delete(`https://6390acc765ff4183111b53e9.mockapi.io/notes/${id}`);
fetchNotes();
history("/notes");
};
...
Returned response Example*:
const Notes = () => {
const history = useNavigate();
const [apiData, setApiData] = useState([]);
useEffect(() => {
axios
.get(`https://6390acc765ff4183111b53e9.mockapi.io/notes`)
.then((getData) => {
setApiData(getData.data);
});
}, []);
const onDelete = async (id) => {
const getData = await axios
.delete(`https://6390acc765ff4183111b53e9.mockapi.io/notes/${id}`);
setApiData(getData.data);
history("/notes");
};
...
*Note: This requires updating the backend code to return the updated data in the response.

Show the previous content for 5 seconds even if the state updates?

I have a React application which uses a Django backend, I have used webSocket to connect with the backend which updates state when there are some changes. But the changes are very rapid, so only the last changes are visible. I want to show the previous message for a certain time before next message is displayed. Here is my code
import React, { useEffect, useState, useRef } from "react";
const Text = () => {
const [message, setMessage] = useState("");
const webSocket = useRef(null);
useEffect(() => {
webSocket.current = new WebSocket("ws://localhost:8000/ws/some_url/");
webSocket.current.onmessage = (res) => {
const data = JSON.parse(res.data);
setMessage(data.message);
};
return () => webSocket.current.close();
}, []);
return <p>{message}</p>;
};
export default Text;
So the message should be visible for certain time (in seconds, for eg - 5 seconds), then the next message should be shown. Any idea how that could be done?
const Text = () => {
const [messages, setMessages] = useState([]);
const currentMessage = messages[0] || "";
const [timer, setTimer] = useState(null);
// webSocket ref missing? ;-)
useEffect(() => {
webSocket.current = new WebSocket("ws://localhost:8000/ws/some_url/");
webSocket.current.onmessage = (res) => {
const data = JSON.parse(res.data);
setMessages((prevState) => [ ...prevState, data.message]);
};
return () => webSocket.current.close();
}, []);
// Remove the current message in 5 seconds.
useEffect(() => {
if (timer || !messages.length) return;
setTimer(setTimeout(() => {
setMessages((prevState) => prevState.slice(1));
setTimer(null);
}, 5000));
}, [messages, timer]);
return <p>{currentMessage}</p>;
};
You can create a custom hook to handle the message transition. Pass as argument the desired time you want to wait before showing the next message. You can use it in other parts of your code:
useQueu.js
const useQueu = time => {
const [current, setCurrent] = useState(null); //--> current message
const queu = useRef([]); //--> messages
useEffect(() => {
const timeout = setTimeout(() => {
setCurrent(queu.current.shift());
}, time);
return () => clearTimeout(timeout);
}, [current]);
const add = obj => {
if (!current) setCurrent(obj); //--> don't wait - render immediately
else {
queu.current.push(obj);
}
};
return [current, add];
};
Text.js
const Text = () => {
const [message, add] = useQue(5000);
const webSocket = useRef(null);
useEffect(() => {
webSocket.current = new WebSocket("ws://localhost:8000/ws/some_url/");
webSocket.current.onmessage = (res) => {
const data = JSON.parse(res.data);
add(data.message); //--> add new message
};
return () => webSocket.current.close();
}, []);
return <p>{message}</p>;
};
Working example

How can I fetch and pass the result to the next fetch in react?

How can I fetch the version and languages and pass them to getChampions function
const [version, setVersion] = useState(0)
const [languages, setLanguages] = useState([])
const [selectedLanguage, setSelectedLanguage] = useState('')
const [champions, setChampions] = useState([])
useEffect(() => {
getVersion().then((version) => setVersion(version))
.then(getLanguages().then(languages => {
setLanguages(languages)
setSelectedLanguage(languages[0])
}))
.then(getChampions(version, selectedLanguage).then(champions => setChampions(champions)))
}, [])
I'm getting the default values from the initialization of useState where version = 0 and languages = []
setState is asynchronous, so if you setState and then call a function with the state immediately after you are not guaranteed to get the current state value. #Yadab's answer resolves this but calling getChampions with the variables from the response rather than the variables from the state.
My personal preference is to use a separate hook to respond to changes in the state. It also seems like getVersion and getLanguages don't depend on each other and can be run simultaneously rather than one after the other.
const App = () => {
const [version, setVersion] = useState(0);
const [languages, setLanguages] = useState([]);
const [selectedLanguage, setSelectedLanguage] = useState("");
const [champions, setChampions] = useState([]);
useEffect(() => {
getVersion().then(setVersion);
}, []); // run once - include [setVersion] if using eslint
useEffect(() => {
getLanguages().then((languages) => {
setLanguages(languages);
setSelectedLanguage(languages[0]);
});
}, []); // run once - can include deps [setLanguage, setSelectedLanguage] for eslint
useEffect(() => {
// only execute if both version and selectedLanguage have already been set
if (version && selectedLanguage) {
getChampions(version, selectedLanguage).then(setChampions);
}
}, [version, selectedLanguage]); // run whenever version or selected language changes
...
You can use async await in a separate function to fetch the version and language and use the fetched version and language to fetch the champions data. Take a look at below example.
const [version, setVersion] = useState(0)
const [languages, setLanguages] = useState([])
const [selectedLanguage, setSelectedLanguage] = useState('')
const [champions, setChampions] = useState([])
const fetchData = async () => {
const versionData = await getVersion();
setVersion(versionData)
const languageData = await getLanguages();
setLanguages(languageData)
setSelectedLanguage(languageData[0])
const championsData = await getChampions(versionData, languageData[0])
setChampions(championsData)
}
useEffect(() => {
fetchData();
}, [])

React + Firebase function looping by accident

I have no idea why is my function looping when I use useStates,
Can anyone figure out the problem.
It loops over and over,this is what appears in my console.log inside the snapshot
`function Classtab() {
const [userName, setuserName] = React.useState(null)
const [userType, setuserType] = React.useState(null)
const [userEmail, setuserEmail] = React.useState(null)
const [userCourse, setuserCourse] = React.useState([])
const [registeredCourse, setregisteredCourse] = React.useState([])
firebase.auth().onAuthStateChanged((user) => {
if(user){
var db = firebase.firestore()
db.collection('user').doc(user.uid)
.get()
.then(snapshot => {
setuserName( snapshot.data().name)
setuserType( snapshot.data().type)
setuserCourse( snapshot.data().course)
setuserEmail( user.email)
console.log(userCourse)
userCourse.map(course => {
db.doc(course).get().then(
snapshot => {setregisteredCourse([...registeredCourse, snapshot.data().name])}
)
}
)
}).catch(error => console.log(error))}else{}
})
return(...)`
You need to move your auth code into useEffect. What's happening right now is that you are running onAuthStateChanged on every render. And each time that returns, it causes another render, causing it to infinitely add more subscriptions.
I've modified your code to prevent the infinite re-renders and allow userCourse to be the correct value in the promise.then function. What it was originally would've had userCourse in the function to always be an empty array (due to the closure).
function Classtab() {
const [userName, setuserName] = React.useState(null);
const [userType, setuserType] = React.useState(null);
const [userEmail, setuserEmail] = React.useState(null);
const [userCourse, setuserCourse] = React.useState([]);
const [registeredCourse, setregisteredCourse] = React.useState([]);
const registeredCourseRef = useRef(registeredCourse);
useEffect(()=>{
registeredCourseRef.current = registeredCourse;
},[registeredCourse])
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
if (user) {
var db = firebase.firestore();
db.collection('user')
.doc(user.uid)
.get()
.then((snapshot) => {
setuserName(snapshot.data().name);
setuserType(snapshot.data().type);
const userCourse = snapshot.data().course;
setuserCourse(userCourse);
setuserEmail(user.email);
console.log(userCourse);
userCourse.map((course) => {
db.doc(course)
.get()
.then((snapshot) => {
setregisteredCourse((registeredCourse)=>[
...registeredCourse,
snapshot.data().name,
]);
});
});
})
.catch((error) => console.log(error));
} else {
}
});
return () => {
unsubscribe();
};
//Need to have registeredCourse in the dependency array
//Or have it in a ref
}, []);
// return(...)
}

React Hook's state not getting updated

I've built a React Hook as follows:
const Index = (props) => {
const [posts, setPosts] = useState([])
useEffect(() => {
const getPosts = async () => {
const posts = await getPostFromWebService()
for (let i of posts) {
setPosts([ ...posts, i ])
}
}
getPosts()
}, [])
// ... remaining code
}
But even if the web service returns 5 posts, only the last posts is getting updated in the posts state. Hence it only receives one post in it, instead of 5.
What am I doing wrong here?
It sounds like you want something like this. Here we would have the useEffect listen for any changes in postCount so that we can trigger your logic to fetch more posts.
const Index = (props) => {
const [posts, setPosts] = useState([])
const [postCount, setPostCount] = useState(0)
useEffect(() => {
const getPosts = async () => {
const newPosts= await getPostFromWebService()
setPosts([...posts, newPosts])
}
}, [postCount])
return(
<div>
<button onClick={() => setPostCount(postCount + 5)}>Get more posts</button>
</div>
)
}

Categories

Resources