When I upload a new image in upload.js and go back to the gallery.js, the require.context doesn't update with the new image import. I see the file in the directory so it is getting uploaded.
I've tried forcing a re-render, but nothing changes. I'm not sure if it has to do with the react-router or something has to happen asynchronously.
//Gallery.js
componentDidMount() {
const interval = setInterval(this.importAll, 3000);
this.setState({interval});
}
componentWillUnmount() {
clearInterval(this.state.interval);
}
importAll() {
const images = [];
//requires all the images from the uploads directory
let context = require.context('../../uploads', false, /\.(png|jpe?g|PNG|JPE?G)$/);
context.keys().forEach(item => {
images.push(context(item));
});
this.setState({images});
}
...
//Upload.js
axios.post('http://localhost:3000/upload', data)
.then (res => {
console.log(res);
this.props.history.push("/");
})
.catch((error) => {
console.log(error);
});
}
...
I am going to go off the assumption that the context is in fact cached by webpack and that is the reason why you don't see updates to it, even with rerender, so you would need to refresh the page to see the change.
That is probably different in production of course.
What I would do is have a addSingleImage method
addSingleImage(imagepath) {
const images = this.state.images;
images.push(imagepath);
this.setState({images});
}
and call that inside of your successful post with the path to the image you have succeeded in uploading.
Related
I have a problem with uploading an image. I have cards and all cards have an image. When I clicked Update Product, Card Details page is opening.
After I click this image, I choose another picture, the photo changes in Front, and when I clicked submit API gives me success and the correct photo URL.
But when I submitted this form, I returned all cards page ( with history.push()), the image was not changed. If I clicked empty cache and hard reload in my browser after the new image was changed. I tried to fix and first of all, I looked network and the response gave me the correct link, I opened a new tab and put the link and a new image opened. Maybe the link between old and new images is the same and useState is not working.
Api gives a new link, useState gives the old link. Both links are the same.
I tried window.location.reload(true), but it did not work first uploading. After first cleaning the cache, the image was uploaded, and after the second uploading, it worked without cleaning the cache.
My codes
It is for getting one card's data and image
const [getPhoto, setGetPhoto] = useState();
useQuery(["PRODUCT_DATA_OVERVIEW_BRANCH", id], () =>
apiService.getAdminProductDetails(id), {
onSuccess: (response) => {
setGetPhoto(response.photoUrl);
},
});
Changing data Api
const { mutate, isLoading } = useMutation(apiService.updateAdminProducts, {
onSuccess: () => {
enqueueSnackbar("Product was updated", { variant: "success" });
history.push("/admin/products");
window.location.reload(true);
},
});
onSubmit
const onSubmit = () => {
const urlProductInfo = new URLSearchParams(getProductData);
let formData = new FormData();
formData.append("photo", getPhoto);
mutate({ urlProductInfo, formData });
};
api
const ApiService = (httpService) => {
updateAdminProducts: async (variables) => {
const { urlProductInfo, formData } = variables;
const response = await httpService.patch(`/product/sme?${urlProductInfo}`, formData);
return response?.data;
},
}
I've just started using NextJs getStaticProps, and static files generated at build time is neat. But my contents just don't stay unchanged, I need static files to be updated but rebuilding the app everytime there's a modification is costly. Is there a way to generate new static files only. getServerSideProps turned out to be taking a big amount of time til first byte.
Depending on type of you content Incremental Statatic Regeneration could be a good solution. But in my experience it introduced other problems when rendering catalog/category pages. As nextjs has no idea if one part of you data is dependent on some other it may render an old catalog page with a link to post or product which no longer exists. This usually happens in combination with a 'fallback' feature for dynamic routes.
It also won't refresh you page right after you made changes so you will see a result only after some time.
Possible workaround is to load posts/product on category page dynamically via ajax. You will lose a part of UX and SEO but on the other hand this solution is relatively easy to maintain.
There is also an option or rather hack to rebuild parts of saved content by directly accessing the cache in custom server. Add 'purge=1' to the page address you want to refreshed.
const { ServerResponse, createServer } = require('http')
const next = require('next')
const { promises } = require('fs')
const path = require('path')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
const purgeByPath = async url => {
const pathname = url == '/' ? '/index' : url
const fullPathname = `.next/server/pages${pathname}`
const fullPathHTML = `${fullPathname}.html`
const fullPathJSON = `${fullPathname}.json`
try {
await promises.unlink(fullPathHTML)
await promises.unlink(fullPathJSON)
} catch (err) {
console.log(`Could not unlink cache files: ${err}`)
}
await app.incrementalCache.cache.del(pathname)
let req = new Request(process.env.BASE_HOST + url)
let res = new ServerResponse(req)
await app.renderToHTML(req, res, url, { _nextDataReq: true })
}
app.prepare().then(() => {
createServer(async (req, res) => {
const url = new URL(req.url, "http://localhost:3000/")
if (req.method == 'POST' && req.url == '/purge-cache') {
let raw = ''
req.on('data', chunk => raw += chunk)
req.on('end', async () => {
const data = JSON.parse(raw)
if (!data || !data.urls) {
res.statusCode = 400
res.end()
return
}
for (let url of data.urls) {
console.log(`Cleaning cache on: ${url}`)
await purgeByPath(url)
}
res.end()
})
} else if (url.searchParams.get('purge') == '1') {
await purgeByPath(req.url)
res.end()
} else {
handle(req, res)
}
}).listen(3000, (err) => {
if (err) throw err
console.log(`> Ready on http://localhost:3000/`)
})
})
The static cache consists of two parts:
Static cache files located at .next/server/pages where each route will probably have two files html and json. Most likely you'll need to delete both.
In-memory cache. Once you app cached a page and saved it to memory it won't go to files on hard drive but instead will load content from memory. So you need to delete it as well.
Cache implementation is not documented an might be swapped or removed in future versions.
This won't work on vercel and has some issues with scaling. You should also add some kind of security token to purge routes.
If I understand it correctly, you are looking for Incremental Statatic Regeneration.
To enable it, you need to add a revalidate time in getStaticProps. As your content is changed and after the revalidation time is reached, a new static page will be generated and served by the server. Depends on how often your content changes, you can change the revalidation time accordingly.
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
// Next.js will attempt to re-generate the page:
// - When a request comes in
// - At most once every 10 seconds
revalidate: 10, // In seconds
}
}
Reference
https://nextjs.org/docs/basic-features/data-fetching#incremental-static-regeneration
I've followed a guide on how to add dynamic content to the cache and and show a error message through a fallback.json file (the error message is supposed to show up in same div where the dynamic content is).
The problem I am facing right now is that the website got multiple pages and I do only want to show the fallback text if I haven't loaded dynamic content on a specific site (the index site on this example). I am currently getting the fallback text on sites I haven't visited when I go offline.
So my question is:
How can the service worker act differently on different sites?
And can I for example use a JS method instead of using a fallback.json file?
Serviceworker.js:
const staticAssets = [
'./',
'./favicon.ico',
'./fallback.json'
];
// Call Install Event
self.addEventListener('install', async e => {
console.log("From SW: Install Event");
const cache = await caches.open('staticCache');
cache.addAll(staticAssets);
});
// Call Fetch Event
self.addEventListener('fetch', async event => {
console.log('Service Worker: Fetching');
const req = event.request;
const url = new URL(req.url);
event.respondWith(networkFirst(req));
});
// Network first for latest articles - if not available get cached
async function networkFirst(req) {
const cache = await caches.open('dynamicContent');
try {
// Try go through the network
const res = await fetch(req,);
// Store to cache
cache.put(req, res.clone());
return res;
} catch (e) {
// IF offline - return from cache
const cachedResponse = await cache.match(req);
// Return cachedresponce first, if no cache found - return fallback.json
return cachedResponse || await caches.match('./fallback.json');
}
}
And my manifest.json file:
{
"articles": [
{
"title": "No articles found!!",
"url": "",
"urlToImage": "",
"description": "Try reload your page once again when you are online."
}
]
}
I managed to solve this by using a IndexedDB. My mistake was trying to save dynamic content in the cache. The better solution for me was to handle and load data in a IndexedDB.
Reference: https://developers.google.com/web/ilt/pwa/working-with-indexeddb
I'm new to nuxt.js so I'm wondering what could be the best way to set up some data via REST api.
I have a store folder like this:
store
-posts.js
-categories.js
-index.js
I've tried to set the data with nuxtServerInit actions in the index.js:
export const actions = {
async nuxtServerInit({ dispatch }) {
await dispatch('categories/setCategories')
await dispatch('posts/loadPosts','all')
}
}
But doesn't works: actions are dispatched (on the server) but data are not set.
So I've tried with fetch but this method is called every time the page where I have to display posts is loaded. Even if, in the general layout, I do this:
<template>
<div>
<Header />
<keep-alive>
<nuxt/>
</keep-alive>
</div>
</template>
So my solution, for now, is to use fetch in this way,
In the page component:
async fetch({store}){
if(store.getters['posts/getPosts'].length === 0 && store.getters['categories/getCategories'].length === 0 ){
await store.dispatch('categories/setCategories')
await store.dispatch('posts/loadPosts','all')
}
}
Also, one thing I noted is that fetch seems not working on the root page component (pages/index.vue)
My solution seems works, but there is maybe another better way to set the data?
There's no out of the box solution for this as it's specific to your requirements/needs. My solution is very similar to yours but instead of checking the size of data array I introduced additional variable loaded in every store module. I only fetch data if loaded is false. This approach is more suitable in apps that have user generated content and require authentication. It will work optimally with SSR and client-side, and it won't try to fetch data on every page visit if user has no data.
You could also simplify your fetch method like this:
async fetch()
{
await this.$store.dispatch('posts/getOnce')
}
Now your posts.js store module will look something like this:
export const state = () => ({
list: [],
loaded: false
})
export const actions = {
async getOnce({ dispatch, state }) {
if (!state.loaded) {
dispatch('posts/get')
}
},
async get({ commit, state }) {
await this.$axios.get(`/posts`)
.then((res) => {
if (res.status === 200) {
commit('set', res.data.posts)
}
})
}
}
export const mutations = {
set(state, posts) {
state.list = posts
state.loaded = true
}
}
I'm trying to get the download url for multiple images, then trigger a change in my app. But... if one of those images doesn't exist for whatever reason, everything fails silently.
Here's the code:
const promises = [];
snapshot.forEach(childSnapshot => {
const child = childSnapshot.val();
const promise = firebase.storage()
.ref(child.songImagePath)
.getDownloadURL()
.catch(err => {
console.log('caught', err);
return "";
})
.then(imageURL => {
return imageURL;
});
promises.push(promise);
});
Promise.all(promises)
.catch(err => {
console.log('caught', err);
})
.then(urls => {
...do something with urls array
});
I'm using child.songImagePath in my database to store the image's location in storage. If ALL paths for ALL images have images, everything works perfectly.
BUT if an upload went awry or for some reason there's no image in the storage location, it fails silently. None of my catches fire. And Promise.all is never resolved.
What's going on here? Is there a way to check for a file's existence before calling getDownloadURL?
EDIT: As #mjr points out, in the documentation they've formatted their error callback slightly differently than I have. This also seems to never fire an error, though:
.then(
imageURL => {
return imageURL;
},
err => {
console.log('caught', err);
return "";
}
);
Firebase Storage JS dev here.
I ran your code with minor changes[1] in Chrome and React Native, and didn't see that behavior.
I see Promise.all always resolving (never failing), with an empty string in the array for invalid files. This is because your .catch handler for getDownloadURL returns an empty string.
For further troubleshooting, it would be useful to know:
version of the firebase JS library you are using
the browser/environment and version
network logs, for example from the network panel in Chrome's dev tools, or similar for other browsers
The firebase-talk Google Group tends to be a better place for open-ended troubleshooting with more back-and-forth.
[1] For reference, here's my code:
const promises = [];
// Swap out the array to test different scenarios
// None of the files exist.
//const arr = ['nofile1', 'nofile2', 'nofile3'];
// All of the files exist.
const arr = ['legitfile1', 'legitfile2', 'legitfile3'];
// Some, but not all, of the files exist.
//const arr = ['legitfile1', 'nofile2', 'nofile3'];
arr.forEach(val => {
const promise = firebase.storage()
.ref(val)
.getDownloadURL()
.catch(err => {
// This runs for nonexistent files
console.log('caught', err);
return "";
})
.then(imageURL => {
// This runs for existing files
return imageURL;
});
promises.push(promise);
});
Promise.all(promises)
.catch(err => {
// This never runs
console.log('caught', err);
})
.then(urls => {
// This always runs
console.log('urls', urls);
});