I'm currently developing an application with express, mongoose and react.
Server rendering:
Get data from my local API (stored in mongoose)
React.renderToString component with data from API as props
This works fine with a static UI. However, now I would like to have some interaction in the UI. Therefore I have to render the same component with the same props on the client side again. Now I'm struggling around about the best way to get my mongoose data (props) on client side... Is it really necessary to save all my json data from mongoose in a script tag and then read it out like this?
if (typeof window !== 'undefined') {
var props = JSON.parse(document.getElementById('props').innerHTML);
React.render(MyComponent(props), document.getElementById('reactMarkup'));
}
Moreover, another way would be to make an ajax call in the componentDidMount function. Which way would you prefer? It would be great if you could help me doing that without the script tag or ajax call... :)
If you want React to transparently upgrade to an SPA on the client side, you need to construct your top-level component with the same props on the client as you did on the server. The most common approach is the one you already mentioned: serialize the props you used and pass them to the host page in a script block or similar.
If you do the Ajax request in componentDidMount, you'll render once with no properties, overwriting the prerendered HTML, and then rerender with the correct properties—I would probably avoid this technique.
Related
I have a question. Inside Nuxt's fetch hook I have some asynchronous calls that are performed by Nuxt content API.
Some pieces of this data are then used inside mounted hook.
But while Nuxt handles first request inside fetch, the control flow passes to the mounted hook and hence there's no needed data.
Yes, I tried uses something like if (!this.$fetchState.pending) return; but obviously mounted is called only once. Does anybody knows how can I force Nuxt to wait? Btw, the app is using static site generation and the component has property fetchOnServer set to false.
In options api,its obvious where we write code for fetching the data from server in mounted method.
With composition api,i am confused as the setup method is the one that loads first before the onMounted hook.
Check their docs: https://vuejs.org/guide/essentials/lifecycle.html#lifecycle-diagram
If you are doing DOM related actions, you would want to do it in onMounted() hooks, because setup() doesn't have access to DOM yet.
So I would probably do it in onMounted() methods since I would probably store result from API to component data or may update DOM as a side-effect.
I want to get access to this.$el in asyncData.
I use a database to store translations.
I want to get a list of translations that are used on the current page.
Then I will send a request to the server to receive them.
After that, I will merge it.
i18.mergeLocaleMessage( locale, message )
How to do it ?
You can access i18n with something like this, no need to access the template for this use case
asyncData ({ app }) {
console.log(app.i18n.t('Hello'))
}
Looking at the lifecycle of Nuxt, asyncData will happen before any template is generated at all, so it is impossible with asyncData.
And even if it was possible with some hacky trick, it would be a bit strange to have to look inside of your template to then have some logic for i18n fetching.
Why not getting a computed nested object and loop on this through your template, after you have fetched all of your required translations ?
Also, you're using asyncData + an API call each time ? So, for every page: you will stop the user, let him wait for the API call and then proceed ?
Latest point, if you are on your page and you hit F5, then asyncData hook will not be triggered. Just to let you know about this caveat.
Alternative solutions:
using the fetch() hook and display a loader until you have fetched all your translations, still better to not rely on the content of the template. This will work even on F5 and can produce a more smooth experience (by not blocking the navigation).
getting your i18n whole translations globally, at some point when your user's connection is idle. Rather than on per-page. Especially because you will need to handle the logic of not fetching some translations that you already have (if the user visits a page twice).
I have a vue spa app. I fetch data for the page in the created hook
My question is how can i avoid fetching data again for the page that has been previously navigated away from if back button is pressed
EDIT
my app is in vue 3 I later used < keep-alive > which worked but am unable to clear the cache even when using max prop on the < keep-alive :max="2" > component
Quite simply, by caching it.
There are several ways to implement some kind of caching, but it largely depends on your current setup.
If you are not using vue-router and the back button causes your page to re-render, you will need to persist the data some other way, such as by using localStore. If you're using vuex, and you want to persis the data, you can try vuex-persistedstate. I'm assuming though that you are not looking to persist the data and the page is not re-loading on redirect.
If you are already using vuex, you can cache the data in the store.
Another option is to use the keep-alive component
If you don't want to rely on a 3rd party tool, you can also wrap your API call with a caching function.
Here is what that may look like. When the data is loaded, it gets cached, and when the cache exists, the cached result is returned as a promise, so that the method returns the same type either way.
let getMyDataCache = null;
export const getMyData(){
if (getMyDataCache) return new Promise((resolve)=>resolve(getMyDataCache));
return fetch('http://example.com/movies.json')
.then(response => response.json())
.then(data => {
getMyDataCache = data;
return getMyDataCache;
});
}
You could use vuex to store the state for your page - then in your created() hook, check if the state has been populated before fetching the data.
To clarify (since this answer is getting downvoted and I'm not sure why): you need to store the fact that you have already fetched your data somewhere. You can't do it inside your component since it will be remounted when you return to the page. If you have a state management pattern in place (e.g. with Vuex), you can store your data there so that when the component is remounted, you can check your store to see if the data has already been fetched before you try to fetch it again.
If you are using vue-router, the answers on this thread might help you instead: How to make certain component "keep-alive" with router-view in Vue 3?
I'm using Relay with React Native and have a problem during login & logout.
After login or logout, Relay keeps the store from the previous user. To solve this I use Relay.Renderer and Relay.Environment. As in, in each Renderer I put singleton object of Environment.
The problem is that I previously did a mutation on object of Relay.Store, as in
Relay.Store.commitUpdate(new CreateProfile(), callback).
Now it doesn't work. I guess this is because Relay.Store doesn't know anything about server endpoints. But Relay.Environment does.
And now I'm using something like this this.props.relay.commitUpdate(new CreateProfile(), callback). It works pretty well when the parent component is wrapped as Relay.Container, so it has relay object in props.
But what should I do in components which are not Relay.Containers and don't have Relay object in props?
Relay.Store is a globally accessible singleton instance of Relay.Environment and Relay.Store.commitUpdate() updates data in that global environment. But since you're using your own instance of Relay.Environment, to update it you need to use this.props.relay.commitUpdate(), as you noted. This updates the environment the container was rendered with.
If need to make mutations from child components of containers, that are not wrapped in a Relay.Container, there are two ways to do that. You could simply pass the relay prop to them, so in the render function of your container you would have:
<Child relay={this.props.relay} />
However, since those plain components are not in a Relay container, they don't currently need to know anything about Relay. If you want to keep them that way, you could write the method that does the update in your container component like this:
onCreateProfile = () => {
this.props.relay.commitUpdate(new CreateProfile());
};
and only pass a callback to your child component in render:
<Child onCreateProfile={this.onCreateProfile} />
If you need to make a mutation from a component that does not have a Relay.Container above it in the component hierarchy at all, you could create the Relay.Environment in a shared root component higher up and pass it down using props (or pass a callback using the strategy shown above).