Not able to set state in react properly - javascript

I have this function which is setting the user
const [user, setUser] = useState(null)
const getUser = async () => {
setIsAuth(false)
setLoading(true)
try {
let temp = await axiosURL.get('/api/users/me')
temp = temp.data
if (temp) {
setUser(temp)
setIsAuth(true)
}
} catch (error) {
console.log(error)
}
setLoading(false)
}
After setting the user I am fecthing the data acording to the user
const getAllScopeData = async () => {
setLoading(true)
await getUser()
console.log(user)
await getScopeOneData()
await getScopeTwoData()
await getScopeThreeData()
setLoading(false)
}
But when I am running getAllScopeData() The user details is fetching correctly and logging in the console but user from the state is not changing from null . So I am able to fetch further data .

the problem I see it's here
const getAllScopeData = async () => {
setLoading(true)
await getUser()
console.log(user)
await getScopeOneData()
await getScopeTwoData()
await getScopeThreeData()
setLoading(false)
}
even if you wait for the function getUser your state is not update yet
You should use useEffect like this
useEffect(async() => {
if(!user) return;
setLoading(true)
await getScopeOneData()
await getScopeTwoData()
await getScopeThreeData()
setLoading(false)
}, [user])

Related

Getting 1000+ reads with a useEffect

How do I properly call a function in a useEffect? Only want to call the function on page load.
I've got multiple functions that are being called in a use effect but the way it's set up I'm getting 1000+ reads and maxing out my quota instantly.
const [user, loading] = useAuthState(auth);
const [myDisplayName, setMyDisplayName] = useState("");
const [projectList, setProjectList] = useState([])
useEffect(()=>{
if(!user) navigate("/login")
fetchUserName();
fetchTotalProjects();
},[])
const fetchUserName = async () => {
try {
const query = await db
.collection("user")
.where("uid", "==", user?.uid)
.get();
const data = await query.docs[0].data();
setMyDisplayName(data.firstName);
} catch (err) {
console.error(err);
}
};
const fetchTotalProjects = async () => {
try {
const query = await db
.collection("projects")
.where("uid", "==", user?.uid)
.get()
.then((snapshot) => {
const tempData = [];
snapshot.forEach((doc) => {
const data = doc.data();
tempData.push(data);
});
setProjectList(tempData);
});
const data = await query.docs[0].data();
setProjectList(data);
} catch (err) {
console.error(err);
}
};
Fixed the issue. Getting the UID from Firebase was coming back undefined the first 3 -5 times and then it would get it, meaning the useEffect would fire until the data was retrieved.
Solution: Get your user uid from Firebase like this
const user = auth?.currentUser;

React-PWA problems with correct caching

I have sw.js which stores data in cache storage.
And there is a dataGrid that displays a list of users.
I want to add users and immediately see the changes, without sw.js everything works fine.
When I use the get api, I always get the cached response until I clear the cache and reload the page.
The cache is not updating.
How should i change this code to make it work correctly?
requests:
export const fetchUsers = createAsyncThunk(
"users/fetchUsers", async () => {
const response = await axiosInstance.get("api/users");
return response.data;
});
export const addNewUser = createAsyncThunk(
'users/addNewUser', async (newUser) => {
const response = await axiosInstance.post("api/users", newUser)
return response.data
})
sw.js
const staticCacheName = 'static-cache-v0';
const dynamicCacheName = 'dynamic-cache-v0';
const staticAssets = [
'./',
'./index.html',
'./images/icons/icon-128x128.png',
'./images/icons/icon-192x192.png',
'./offline.html',
'./css/main.css',
'./js/app.js',
'./js/main.js',
'./images/no-image.jpg'
];
self.addEventListener('install', async event => {
const cache = await caches.open(staticCacheName);
await cache.addAll(staticAssets);
console.log('Service worker has been installed');
});
self.addEventListener('activate', async event => {
const cachesKeys = await caches.keys();
const checkKeys = cachesKeys.map(async key => {
if (![staticCacheName, dynamicCacheName].includes(key)) {
await caches.delete(key);
}
});
await Promise.all(checkKeys);
console.log('Service worker has been activated');
});
self.addEventListener('fetch', event => {
console.log(`Trying to fetch ${event.request.url}`);
event.respondWith(checkCache(event.request));
});
async function checkCache(req) {
const cachedResponse = await caches.match(req);
return cachedResponse || checkOnline(req);
}
async function checkOnline(req) {
const cache = await caches.open(dynamicCacheName);
try {
const res = await fetch(req);
await cache.put(req, res.clone());
return res;
} catch (error) {
const cachedRes = await cache.match(req);
if (cachedRes) {
return cachedRes;
} else if (req.url.indexOf('.html') !== -1) {
return caches.match('./offline.html');
} else {
return caches.match('./images/no-image.jpg');
}
}
}

useState set call not reflecting change immediately prior to first render of app

New to React Hooks and unsure how to solve. I have the following snippet of code within my App.js file below.
What I am basically trying to achieve is to get the user logged in by calling the getUser() function and once I have the user id, then check if they are an authorised user by calling the function checkUserAccess() for user id.
Based on results within the the validIds array, I check to see if it's true or false and set authorised state to true or false via the setAuthorised() call.
My problem is, I need this to process first prior to performing my first render within my App.js file.
At the moment, it's saying that I'm not authroised even though I am.
Can anyone pls assist with what I am doing wrong as I need to ensure that authorised useState is set correctly prior to first component render of application, i.e. path="/"
const [theId, setTheId] = useState('');
const [authorised, setAuthorised] = useState(false);
const checkUserAccess = async (empid) => {
try {
const response = await fetch("http://localhost:4200/get-valid-users");
const allUsers = await response.json();
const validIds = allUsers.map(({ id }) => id);
const isAuthorised = validIds.includes(empid);
if (isAuthorised) {
setAuthorised(true)
} else {
setAuthorised(false)
}
} catch (err) {
console.error(err.message);
}
}
const getUser = async () => {
try {
const response = await fetch("http://localhost:4200/get-user");
const theId= await response.json();
setTheId(theId);
checkUserAccess(theId);
} catch (err) {
console.error(err.message);
}
}
useEffect(() => {
getUser();
}, []);
Unless you are wanting to partially render when you get the user ID, and then get the access level. There is no reason to have multiple useState's / useEffect's.
Just get your user and then get your access level and use that.
Below is an example.
const [user, setUser] = useState(null);
const checkUserAccess = async (empid) => {
const response = await fetch("http://localhost:4200/get-valid-users");
const allUsers = await response.json();
const validIds = allUsers.map(({ id }) => id);
const isAuthorised = validIds.includes(empid);
return isAuthorised;
}
const getUser = async () => {
try {
const response = await fetch("http://localhost:4200/get-user");
const theId= await response.json();
const access = await checkUserAccess(theId);
setUser({
theId,
access
});
} catch (err) {
console.error(err.message);
}
}
useEffect(() => {
getUser();
}, []);
if (!user) return <div>Loading</div>;
return <>{user.theId}</>
This way it should work
but keep in mind that you must render your app only if theId in the state is present, which will mean your user is properly fetched.
const [state, setState] = useState({ theId: '', isAutorized: false })
const getUser = async () => {
try {
const idResp = await fetch("http://localhost:4200/get-user");
const theId = await idResp.json();
const authResp = await fetch("http://localhost:4200/get-valid-users");
const allUsers = await authResp.response.json();
const validIds = allUsers.map(({ id }) => id);
const isAuthorised = validIds.includes(theId);
setState({ theId, isAuthorised })
} catch (err) {
console.error(err.message);
}
}
useEffect(() => {
getUser();
}, []);
if (!state.theId) return <div>Loading</div>;
if (state.theId && !isAuthorized) return <AccessNotAllowed />
return <Home />

React: async and await not working with fetch

I have API on Node server returning JSON like this when called:
{"result":[{"ProductID":1,"ProductName":"iPhone10","ProductDescription":"Latest smartphone from Apple","ProductQuantity":100}]}
I'm trying to display all of that information to user using fetch API with React but no matter what my call returns undefined. Here is my React code:
const [products, setProducts] = useState({})
async function getProducts() {
await fetch(`http://127.0.0.1:5000/listProducts`)
.then(response => response.json())
.then(response=>{
setProducts({products:response.result})
console.log(response.result);
products.map(products =>
<h1>{products.ProductName}</h1>
<h1>{products.ProductDescription}</h1>
)
})
.catch(err=>console.error(err))
}
Function getProducts() is called once when page is loaded. What I'm doing wrong? Thanks in advance.
Try this it will work
const handleFetchData = async () => {
const response = await fetch(`https://api.unsplash.com/photos/random?client_id=${process.env.NEXT_PUBLIC_UNSPLASH_API_ACCESS_KEY}`);
const data = await response.json();
console.log(data);
}
useEffect(() => {
handleFetchData();
},[])
Your function is doing it wrong :
The name should be getAndSetProducts or even setProducts / initProducts because it returns a Promise<void> since you don't actually return anything ;
You're setting inside products an object { products: Product[] }, I think you want only Product[] (an array of Products) else you'll have to get products by doing products.products ;
The map is useless, since you don't do anything with the map response, plus the variable products in the map overwrite the one imported (may cause some errors later).
Try to do :
const [products, setProducts] = useState([]); // Array instead of object
async function initProducts() {
await fetch(`http://127.0.0.1:5000/listProducts`)
.then(response => response.json())
.then(response => {
setProducts(response.result);
console.log(response.result);
)
.catch(err => console.error(err));
}
function getProductsHtml() {
return products.map(product =>
<h1>{product.ProductName}</h1>
<h1>{product.ProductDescription}</h1>
);
}
You can call initProducts when component initialize and return getProductsHtml inside your jsx render.
Try this...
const [products, setProducts] = useState({})
React.useEffect(() => {
const fetchData = async () => {
const result = await fetch('http://127.0.0.1:5000/listProducts')
// console log here to determine how to set products
console.log(result)
setProducts(result)
}
fetchData()
}, [])
React.useEffect(() => {
if (!!products) {
// here you could access products!
}
}, [products])
if (!products) return null
return products.map((product) => (
<>
<h1>{products.ProductName}</h1>
<h1>{products.ProductDescription}</h1>
</>
))
If you are using Async, then you can use response.status as shown below
const response = await fetch("URL", {
body:BODY_DATA,
method:'POST',
headers: { "Content-Type": "application/json"
});
if(response.status === 200){
// Complete your action
} else {
// Show error
}
you can't use async await with .then() you should only use async await or .then() only

struggle to wrap promise with async await

I got unexpected identifier but not sure what is the mistake. I'm using fetch which is already a promise.
async getUsers = () => {
const resp = await fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
return resp
}
getUsers().then(users => console.log(users))
Notice the position of the async keyword:
Not:
async getUsers = () => {
But:
getUsers = async () => {
Run:
getUsers = async () => {
const resp = await fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
return resp;
};
getUsers().then(users => console.log(users))
As per comments:
Should I chain then() in getUsers()? async/await suppose to eliminate then() am I right?
Yes, you can await any Promise. Or use both .then() sometimes and await at others (like does the code above). But you could just use async/await as well.
The example below uses no .then():
getUsers = async () => {
const resp = await fetch('https://jsonplaceholder.typicode.com/posts/1')
return resp.json();
};
(async () => {
// notice to use the await keyword, the code must be wrapped in an async function
const users = await getUsers();
console.log(users);
})();
Aside from the typo in the async word pointed by #acdcjunior, you're mixing async / await with the usual promise handling (.then()) which is not wrong but kind of defeats the point. Using only async / await would look like:
const getUsers = async () => {
const resp = await fetch('https://jsonplaceholder.typicode.com/posts/1');
return resp.json();
}
async function fetchUsers() {
try {
const users = await getUsers();
console.log(users);
} catch(err) {
console.log(err);
}
}
fetchUsers();
you have the syntax wrong:
const getusers = async () => {
...
}
const is optional
Your syntax of declaring your function is wrong, here's some explanation.
If getUsers is a method of a react component class the syntax should be :
getUsers = async () => {
const resp = await fetch(
'https://jsonplaceholder.typicode.com/posts/1'
).then(response => response.json());
return resp;
};
or :
async getUsers() {
const resp = await fetch(
'https://jsonplaceholder.typicode.com/posts/1'
).then(response => response.json());
return resp;
};
If it's outside of a react component class or in a stateless arrow function component you can use this syntax :
const getUsers = async () => {
const resp = await fetch(
'https://jsonplaceholder.typicode.com/posts/1'
).then(response => response.json());
return resp;
};
const getUsers = async () => {
try {
const resp = await fetch('https://jsonplaceholder.typicode.com/posts/1');
return resp.json();
} catch(e) {
console.error(e)
}
}
(async () => {
const users = await getUsers();
console.log(users)
})()
Use this, and run

Categories

Resources