React-PWA problems with correct caching - javascript

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');
}
}
}

Related

How to do cleanup in useEffect React?

Usually I do useEffect cleanups like this:
useEffect(() => {
if (!openModal) {
let controller = new AbortController();
const getEvents = async () => {
try {
const response = await fetch(`/api/groups/`, {
signal: controller.signal,
});
const jsonData = await response.json();
setGroupEvents(jsonData);
controller = null;
} catch (err) {
console.error(err.message);
}
};
getEvents();
return () => controller?.abort();
}
}, [openModal]);
But I don't know how to do in this situation:
I have useEffect in Events.js file that get events from function and function in helpers.js file that create events on given dates except holidays (holiday dates fetch from database).
Events.js
useEffect(() => {
if (groupEvents.length > 0) {
const getGroupEvents = async () => {
const passed = await passEvents(groupEvents); // function in helpers.js (return array of events)
if (passed) {
setEvents(passed.concat(userEvents));
} else {
setEvents(userEvents);
}
};
getGroupEvents();
}
}, [groupEvents, userEvents]);
helpers.js
const passEvents = async (e) => {
try {
const response = await fetch(`/api/misc/holidays`, {
credentials: 'same-origin',
});
const jsonData = await response.json();
const holidays = jsonData.map((x) => x.date.split('T')[0]); // holiday dates
return getEvents(e, holidays); // create events
} catch (err) {
console.error(err.message);
}
};
You can either not clean up, which really is also fine in many situations, but if you definitely want to be able to abort the in-flight request, you will need to create the signal from the top-level where you want to be able to abort, and pass it down to every function.
This means adding a signal parameter to passEvents.
Remove ?:
return () => controller.abort();
Like to this: https://www.youtube.com/watch?v=aKOQtGLT-Yk&list=PL4cUxeGkcC9gZD-Tvwfod2gaISzfRiP9d&index=25
Is it what you want?

I want to use axios's return to global state ,but Promise { <pending> }

const Board = () => {
...
const {selected} = useSelector(state => state.board);
// In Redux reducer ->
// const initialState = {
// selected : {}
// }
const getOnePost = async (id) => {
try {
const response = await axios.get(`/api/v1/post/${id}`);
const getOnePostData = await response.data;
selected = getOnePostData //I want to use Redux to use global state...
console.log(selected) //TypeError... and How to use global state..?
} catch(error) {
alert(error.response.data.message);
return Promise.reject(error)
}
}
const postClickHandler = (postId) =>
{
if(postId) {
// dispatch(boardSelected(postId));
getOnePost(postId)
}
}
...
}
This code uses axios.get to receive post information.
and I want to use api's return to global state(Redux state).
const getOnePost = async (id) => {
try {
const response = await axios.get(`/api/v1/post/${id}`);
const getOnePostData = await response.data;
return getOnePostData //return data
} catch(error) {
alert(error.response.data.message);
return Promise.reject(error)
}
}
const postClickHandler = (postId) =>
{
if(postId) {
getOnePost(postId).then((response)=>{
return{...selected, selected: response} //But Seleted: {}
})
}
}
Axios is a Promised-based JavaScript library that is used to send HTTP requests,
you have to get the promise result in the then method .
try this
const Board = () => {
...
const {selected} = useSelector(state => state.board);
// In Redux reducer ->
// const initialState = {
// selected : {}
// }
const getOnePost = async (id) => {
try {
const response = await axios.get(`/api/v1/post/${id}`);
response.then(function (rec) {
selected = rec.data //
//here update you state
console.log(selected)
}).catch(function (error) {
console.log(error);
});
} catch(error) {
alert(error.response.data.message);
return Promise.reject(error)
}
}
const postClickHandler = (postId) =>
{
if(postId) {
// dispatch(boardSelected(postId));
getOnePost(postId)
}
}
...
}
You can handle promise by using .then()
Try to initialize selected like below :
const response = await axios.get(`/api/v1/post/${id}`).then( response => {
selected = response
})
I don't know what kind of data is it. So, try to log response data, and use it.

Why am I getting a network error on page refresh? (get request)

I'm making a get request to an API in a useEffect(). When I navigate to the page from the homepage it loads fine, but as soon as i refresh the page http://localhost:3000/coins/coin I get a Unhandled Runtime Error: Error: Network Error.
export async function getServerSideProps({ query }) {
const id = query;
return {
props: { data: id },
};
}
function index({ data }) {
const coinURL = data.id; // bitcoin
const apiEndpoint = `https://api.coingecko.com/api/v3/coins/${coinURL}`;
const [currentUser, setCurrentUser] = useState();
const [coinData, setCoinData] = useState([]);
useEffect(() => {
const getData = async () => {
const res = await axios.get(apiEndpoint);
const { data } = res;
setCoinData(data);
};
const getCurrentUser = async () => {
const res = await axios.get(
`http://localhost:5000/api/users/${session?.id}`
);
const { data } = res;
setCurrentUser(data);
};
getData();
getCurrentUser();
}, [coinData, currentUser]);
}
Why does this happen?
I'm recommending to do something like this:
const getData = async () => {
try {
const res = await axios.get(apiEndpoint);
const { data } = res;
setCoinData(data);
} catch(err) {
console.log(err)
}
};
const getCurrentUser = async () => {
try {
const res = await axios.get(
`http://localhost:5000/api/users/${session?.id}`
);
const { data } = res;
setCurrentUser(data);
} catch(err) {
console.log(err)
}
};
useEffect(() => {
getData();
getCurrentUser();
}, [coinData, currentUser]);
if you do so, you will be able to view the exact error and fix it.

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 />

nodejs javascript promise resolve

I can't seem to figure out how to save the results of SomeQuery promise. Essentially I would like to take the value in res and pipe it into parseQuery function and return the final results. How do I make the parsed result accessible to an APIs response.
const neo4j = require('neo4j-driver')
var parser = require('parse-neo4j')
const astria_queries = require('./astriaQueries')
const uri = 'bolt://astria_graph:7687'
const user = 'xxx'
const password = 'xxx'
const someQuery = (query) => {
// run statement in a transaction
const driver = neo4j.driver(uri, neo4j.auth.basic(user, password))
const session = driver.session({ defaultAccessMode: neo4j.session.READ })
const tx = session.beginTransaction()
tx.run(query)
.then((res) => {
// Everything is OK, the transaction will be committed
parseQuery(res)
})
.then(() => {
// Everything is OK, the transaction will be committed
})
.catch((e) => {
// The transaction will be rolled back, now handle the error.
console.log(e)
})
.finally(() => {
session.close()
driver.close()
})
}
const parseQuery = (result) => {
try {
const test = parser.parse(result)
console.log(test)
} catch (err) {
console.log(err)
}
}
module.exports = {
someQuery,
}
It finally clicked with me. Here is the solution I came up with. Hopefully it will help others. If there is a better way please let me know. Thank you #fbiville for you help.
async actions
const neo4j = require('neo4j-driver')
var parser = require('parse-neo4j')
const astria_queries = require('./astriaQueries')
const uri = 'bolt://astria_graph:7687'
const user = 'neo4j'
const password = 'neo'
async function getRecords(query) {
// run statement in a transaction
const driver = neo4j.driver(uri, neo4j.auth.basic(user, password))
const session = driver.session({ defaultAccessMode: neo4j.session.READ })
const tx = session.beginTransaction()
try {
const records = await tx.run(query)
const parseRecords = await parseQuery(records)
return parseRecords
} catch (error) {
console.log(error)
} finally {
session.close()
driver.close()
}
}
async function parseQuery(result) {
try {
const parsedRes = await parser.parse(result)
// console.log(parsedRes)
return parsedRes
} catch (err) {
console.log(err)
}
}
// getRecords(astria_queries.get_data_sources)
module.exports = {
getRecords,
}
api send()
exports.get_data_sources = async (req, res) => {
try {
queryFuns.getRecords(astria_queries.get_data_sources).then((response) => {
res.send(response)
})
} catch (error) {
res.status(500).send(error)
console.log(error)
}
}

Categories

Resources