In my NuxtJS SSR project with bootstrap-vue as frontend :
I have some page with template and default component
In component there is asyncData(context) function that makes some deal before component render and mounts
Everything is working fine, except only one thing, I need reload (or refresh) a page after some interval automatically and call asyncData again. Standard js window.location.reload() is not good solution, because it`s reloads fully page. I want refresh in vue style, when only changed components re-rendres. I tried to call $nuxt.refresh() but no any effect
Some code example (cut from real project ) , page "index.vue" :
<template>
<div>
<b-tabs content-class="mt-3">
<b-tab title="Test" active><shdashboard searchtype="test"> </shdashboard></b-tab>
</b-tabs>
</div>
</template>
<script>
export default {
name : "index",
async asyncData (context) {
if(process.server)
{
const host = "http://SOMEHOST";
const http = context.$http;
const data = await http.$get(url);
console.log('in server render')
/*
some logic
commit store and prepare data fot b-tab component
*/
}
},
methods : {
refresh() {
console.log("method refresh")
this.$nuxt.refresh(); // Not any effect.
}
},
mounted() {
console.log("page mounted");
setInterval(()=>{
/*
I need to reload page every 15 sec, and call asyncData() to get new values for
<b-tab> component
*/
this.refresh();
},15000);
}
</script>
What do I wrong ?
One way, if I understand your issue correctly, is to use the Vuex store for storing whatever you are fetching in fetch/asyncData. That way you don't need to worry about the internals of Nuxt.js, and what hooks are triggered when.
I.e. something like this:
export default {
computed: {
someData () {
return this.$store.state.someData
},
},
async asyncData ({ store, $axios }){
let data = await $axios.$get(endpoint)
store.commit('setSomeData', data)
},
}
Then just use {{ someData }} in your template!
constant file -> constant.js
export default {
CITY: 'Banglore',
STATE: 'Karnataka'
}
Show Default City Name -> address.jsx
import React from "react";
import CONSTANTS from "./constants";
import "./styles.css";
const Address = () => {
return (
<div className="App">
<p> City : {`${CONSTANTS.CITY}`} </p>
<p> State : {`${CONSTANTS.STATE}`} </p>
</div>
);
};
export default Address;
expected output:
city: banglore
state: karnataka
we are importing the constant values from constant.js file, now the problem is we have to make one API call which may return overriding values for the constant keys
example of API response:
{
CITY: 'Mysuru'
}
then CITY is constant file should override with the new value which come after API response and rest other keys should keep their values same.
expected output:
city: Mysuru
state: karnataka
this the basic problem case for me, actually our application already in mid phase of development and more than 500+ constant keys are imported in 100+ components.
1. we are using redux in our application
2. we have to call API only once that should effects to all the components
what is the best way to achieve this problem, how can i override my constant files once i make the call to backend, Thank you
Since the question has changed, so does my answer (keeping the original one below). I'd suggest to rebuild the constants file to either return the constants or from Localstorage. However, be aware that the current components will not be rebuild using this approach. Only thing that'll trigger a rebuild is either use Redux for this or local state management.
const data = {
CITY: 'Banglore',
STATE: 'Karnataka'
}
const getData = () => {
let localData = window.localStorage.getItem('const-data');
if (!localData) {
axios.get('url')
.then(response => {
localData = {...response.data};
window.localStorage.setItem('const-data', JSON.stringify({...localData}));
});
}
return localData ? localData : data;
}
export default getData();
Original answer:
This is how I'd solve it using local state. It was some time ago since I was using Redux. Though the same principle should apply instead of putting the data in local state, put it in the Redux.
I prefer the simplicity of using local state whenever there's no need to share data over multiple components.
import React, { useEffect } from "react";
import CONSTANTS from "./constants";
import "./styles.css";
const Address = () => {
const [constants, setConstants] = useState({...CONSTANTS});
useEffect(() => {
//api call
//setConstants({...apiData});
}, []);
return (
<div className="App">
<p> City : {`${constants.CITY}`} </p>
<p> State : {`${constants.STATE}`} </p>
</div>
);
};
export default Address;
I m new at redux and I have integrated it with my react app , but I have a note about a small test .
At the next example I see that the value of user added on my second click.
the reducer:
const initialState = {
user: '',
password: ''
}
export const admin = (state = initialState, action) => {
switch (action.type) {
case 'admin':
return state = {
user: action.user,
password: action.password
}
default:
return initialState;
}
}
action :
const adminAction = (user, password) => {
return {
type: 'admin',
user: user,
password: password
}
}
export default adminAction;
changing state in store.
const admin = useSelector(state => state.admin);
const dispatch = useDispatch();
var user,pass = '';
const adminChangeUsername = (event) => {
user = event.target.value;
}
const adminChangePassword = (event) => {
pass = event.target.value;
}
const click= (event) => {
event.preventDefault();
dispatch(adminAction(user,pass));
console.log(store.getState())
console.log(admin.user)
}
the click void associated to a button .
when doing the first click this is what happens :
the value has changed in the store but the admin.user value is still empty.
when clicking a second time :
The store values have updated
The admin value has been added .
My question is why does only the second click trigger the retrieval of the value from store?
The reason why you are seeing the old value of admin in the click function is due to closures which cause the stale props and state problem. How do JavaScript closures work?
Every function in JavaScript maintains a reference to its outer
lexical environment. This reference is used to configure the execution
context created when a function is invoked. This reference enables
code inside the function to "see" variables declared outside the
function, regardless of when and where the function is called.
In a React function component, each time the component re-renders, the closures are re-created, so values from one render don't leak into a different render. It's for this same reason why any state you want should be within useState or useReducer hooks because they will remain past re-renders.
More information on the react side of it in React's FAQ
In addition to the main answer for the question, you should useState for the values of user and pass in your component. Otherwise the values don't stay between re-renders.
You could otherwise go without using controlled components, but not use a weird mix of both.
Working example below:
const initialstate = {
user: '',
password: '',
};
const adminReducer = (state = initialstate, action) => {
switch (action.type) {
case 'admin':
return (state = {
user: action.user,
password: action.password,
});
default:
return initialstate;
}
};
const adminAction = (user, password) => {
return {
type: 'admin',
user: user,
password: password,
};
};
const store = Redux.createStore(Redux.combineReducers({ admin: adminReducer }));
const App = () => {
const store = ReactRedux.useStore();
const admin = ReactRedux.useSelector((state) => state.admin);
const dispatch = ReactRedux.useDispatch();
const [user, setUser] = React.useState('');
const [pass, setPass] = React.useState('');
const adminChangeUsername = (event) => {
setUser(event.target.value);
};
const adminChangePassword = (event) => {
setPass(event.target.value);
};
const click = (event) => {
event.preventDefault();
dispatch(adminAction(user, pass));
console.log('store has the correct value here', store.getState());
console.log('admin is the previous value of admin due to closures:', admin);
};
return (
<div>
<input
onChange={adminChangeUsername}
value={user}
placeholder="username"
/>
<input
onChange={adminChangePassword}
value={pass}
type="password"
placeholder="password"
/>
<button onClick={click}>submit</button>
<p>This shows the current value of user and password in the store</p>
<p>User: {admin.user}</p>
<p>Pass: {admin.password}</p>
</div>
);
};
ReactDOM.render(
<ReactRedux.Provider store={store}>
<App />
</ReactRedux.Provider>,
document.querySelector('#root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js" integrity="sha256-7nQo8jg3+LLQfXy/aqP5D6XtqDQRODTO18xBdHhQow4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js" integrity="sha256-JuJho1zqwIX4ytqII+qIgEoCrGDVSaM3+Ul7AtHv2zY=" crossorigin="anonymous"></script>
<div id="root"/>
Example with original vars instead of useState. You'll notice in this case by using the vars and keeping the value prop on the inputs, it prevents entry to the password input. The reason why it "works" on the user input is because the user variable started off as undefined rather than an empty string.
const initialstate = {
user: '',
password: '',
};
const adminReducer = (state = initialstate, action) => {
switch (action.type) {
case 'admin':
return (state = {
user: action.user,
password: action.password,
});
default:
return initialstate;
}
};
const adminAction = (user, password) => {
return {
type: 'admin',
user: user,
password: password,
};
};
const store = Redux.createStore(Redux.combineReducers({ admin: adminReducer }));
const App = () => {
const store = ReactRedux.useStore();
const admin = ReactRedux.useSelector((state) => state.admin);
const dispatch = ReactRedux.useDispatch();
var user,pass='';
const adminChangeUsername = (event) => {
user = event.target.value;
};
const adminChangePassword = (event) => {
pass = event.target.value;
};
const click = (event) => {
event.preventDefault();
dispatch(adminAction(user, pass));
console.log('store has the correct value here', store.getState());
console.log('admin is the previous value of admin due to closures:', admin);
};
return (
<div>
<input
onChange={adminChangeUsername}
value={user}
placeholder="username"
/>
<input
onChange={adminChangePassword}
value={pass}
type="password"
placeholder="password"
/>
<button onClick={click}>submit</button>
<p>This shows the current value of user and password in the store</p>
<p>User: {admin.user}</p>
<p>Pass: {admin.password}</p>
</div>
);
};
ReactDOM.render(
<ReactRedux.Provider store={store}>
<App />
</ReactRedux.Provider>,
document.querySelector('#root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js" integrity="sha256-7nQo8jg3+LLQfXy/aqP5D6XtqDQRODTO18xBdHhQow4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js" integrity="sha256-JuJho1zqwIX4ytqII+qIgEoCrGDVSaM3+Ul7AtHv2zY=" crossorigin="anonymous"></script>
<div id="root"/>
Example with fully uncontrolled inputs. This doesn't need the vars at all:
const initialstate = {
user: '',
password: '',
};
const adminReducer = (state = initialstate, action) => {
switch (action.type) {
case 'admin':
return (state = {
user: action.user,
password: action.password,
});
default:
return initialstate;
}
};
const adminAction = (user, password) => {
return {
type: 'admin',
user: user,
password: password,
};
};
const store = Redux.createStore(Redux.combineReducers({ admin: adminReducer }));
const App = () => {
const store = ReactRedux.useStore();
const admin = ReactRedux.useSelector((state) => state.admin);
const dispatch = ReactRedux.useDispatch();
const click = (event) => {
event.preventDefault();
const user = event.currentTarget.user.value;
const pass = event.currentTarget.pass.value;
dispatch(adminAction(user, pass));
console.log('store has the correct value here', store.getState());
console.log('admin is the previous value of admin due to closures:', admin);
};
return (
<div>
<form onSubmit={click}>
<input name="user" placeholder="username" />
<input name="pass" type="password" placeholder="password" />
<button type="submit">submit</button>
</form>
<p>This shows the current value of user and password in the store</p>
<p>User: {admin.user}</p>
<p>Pass: {admin.password}</p>
</div>
);
};
ReactDOM.render(
<ReactRedux.Provider store={store}>
<App />
</ReactRedux.Provider>,
document.querySelector('#root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js" integrity="sha256-7nQo8jg3+LLQfXy/aqP5D6XtqDQRODTO18xBdHhQow4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js" integrity="sha256-JuJho1zqwIX4ytqII+qIgEoCrGDVSaM3+Ul7AtHv2zY=" crossorigin="anonymous"></script>
<div id="root"/>
Why closures are relevant to the post
Because of the synchronous nature of the original code, it's possible to say that the reason the admin.user value that's logged isn't the new value is simply because the component hadn't re-rendered yet. However, the point remains that the admin value for that render is set in stone and will never change due to a closure. Even if React rendered synchronously with the redux state updating, admin wouldn't change.
It's for reasons like this that it's important to think about the closures and how everything fully works even in the simpler cases so you don't mess up in the more complicated ones.
For example of how incorrect mental models can hinder you, imagine that you assume that the reason the console.log(admin.user) doesn't show the correct value in the original example is solely because the component hadn't re-rendered yet. You might assume that putting a timeout to wait for the re-render would let you see the new value.
Imagine trying to add an auto-save functionality where you want to save the current value of admin to localstorage or an API every 10 seconds. You might put an effect with an interval of 10 seconds that logs admin with an empty dependency array because you want it to not reset until the component unmounts. This is clearly incorrect because it doesn't include the admin value in the dependencies, but it will highlight that no matter how many times you submit the values and change admin in the redux state, the value in this effect never changes because this effect will always be running from the initial value of admin on the first render.
useEffect(()=>{
const intervalId = setInterval(()=>{console.log(admin)},10000);
return ()=>clearInterval(intervalId);
},[])
Closures are the overall issue and you can easily get in trouble if you have the wrong mental model for how things work.
More information on closures:
A closure is the combination of a function bundled together (enclosed)
with references to its surrounding state (the lexical environment). In
other words, a closure gives you access to an outer function’s scope
from an inner function. In JavaScript, closures are created every time
a function is created, at function creation time.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
As mentioned in the MDN web docs, every time a function is created, the function gains permanent access to the variables in the surrounding code. Essentially, the functions get access to everything within scope for them.
In react function components, each render creates new functions.
In this example, when App is rendered:
const App = ()=>{
const admin = useSelector(state => state.admin);
const dispatch = useDispatch();
var user,pass = '';
const adminChangeUsername = (event) => {
user = event.target.value;
}
const adminChangePassword = (event) => {
pass = event.target.value;
}
const click= (event) => {
event.preventDefault();
dispatch(adminAction(user,pass));
console.log(store.getState())
console.log(admin.user)
}
}
the admin variable is initialized into the scope of App based on the current state of the redux store (i.e. { user: '', password: ''}).
The dispatch variable is initialized to be redux's store.dispatch
user is initialized to undefined
pass is initialized to ''
the adminChangeUsername and adminChangePassword functions are initialized. They have access to (and use) user and pass from the closure that they create.
click is initialized. It has access to (and uses) user, pass, store, and admin from it's closure.
click always only has access to the admin value from the current re-render when it was created because the click function created a closure around it when it was created. Once the click event occurs, and adminAction is dispatched, click won't change which render it was from, so it will have access to the admin variable that was initialized when it was. And each render, the admin variable is re-initialized and is thus a different variable.
Also the main issue here not just the asynchronous nature but the fact
that state values are used by functions based on their current
closures and state updates will reflect in the next re-render by which
the existing closures are not affected but new ones are created. Now
in the current state the values within hooks are obtained by existing
closures and when a re-render happens the closures are updated based
on whether function is recreated again or not
usestate-set-method-not-reflecting-change-immediately - Shubham Khatri
No matter if you put a timeout in the click handler, the admin value will never be the new value because of that closure. Even if React happens to re-render immediately after calling dispatch.
You can use a useRef to create a mutatable value who's current property will remain the same between re-renders, and at that point, the asynchronous nature of React's re-renders actually comes into play because you'd still have the old value for a few more miliseconds before react re-renders the component.
In this example https://codesandbox.io/s/peaceful-neumann-0t9nw?file=/src/App.js, you can see that the admin value logged in the timeout still has the old value, but the adminRef.current value logged in the timeout has the new value. The reason for that is because admin points to the variable initialized in the previous render, while adminRef.current stays the same variable between re-renders. Example of the logs from the code sandbox below:
The only thing that might really surprise you about the order of events in this is that useSelector is called with the new data before the re-render occurs. The reason for this is that react-redux calls the function whenever the redux store is changed in order to determine if the component needs to be re-rendered due to the change. It would be entirely possible to set the adminRef.current property within the useSelector to always have access to the latest value of admin without store.getState().
In this case, it's easy enough to just use store.getState() to get the current redux state if you need access to the latest state outside of the hook's closure. I often tend to use things like that in longer-running effects because store doesn't trigger re-renders when changed and can help performance in performance critical locations. Though, it should be used carefully for the same reason of it being able to always get you access to the latest state.
I used a timeout in this example because that's the best way to highlight the actual nature of the closure's effect in this. Imagine if you will that the timeout is some asynchronous data call that takes a bit of time to accomplish.
The admin object will be fed after the next component rerendering process, after the whole click callback has been executed.
Indeed, it is based on the value provided by useSelector that is triggered ONCE during a rendering.
admin.user being checked in the click callback, the component has not been reredenring yet, thus showing the current empty string value the first crossing.
1) Component is rendered for the first time.
2) useSelector is called.
3) Click is made.
4) Action is dispatched.
5) admin.user is still empty since 2) is has not been run again yet.
6) After click callback finishes, a rerendering is about to be made so useSelector is triggered again, grabbing the admin value!
Moral of the story:
Don't expect value provided from useSelector to be immediately synchronised within the click callback.
It needs rerendering process to happen.
Besides, and just to improve your code, you can replace:
return state = {
user: action.user,
password: action.password
}
by this:
return {
user: action.user,
password: action.password
}
In my Next.js application I have search filters.
Filters consist of checkboxes, and to render these checkboxes I need to fetch (GET) all possible options from the API.
Those filters are available on many pages, so regardless the page where user lands I need to fetch the data for filters immediately and put it in the local storage to avoid further excessive API calls. Putting API call in each page is not an option.
I see the one option is to put the API call in getInitialProps in _app.js, but then according to Next.js docs automatic static optimization will not work and every page in my app will be server-side rendered.
So what is the proper way to fetch such global data in Next.js?
--------UPDATE
So at this moment I've used the next solution: in _app.js I put useEffect React Hook and once the Frontend is ready I am checking whether my data for whole application is in locale storage. If it's not then fetch data from server and put in local storage for further use.
// _app.js
const AppWrapper = ({ children }) => {
const dispatch = useAppDispatch();
useEffect(() => {
dispatch({ type: FRONTEND_LOADED });
loadInitialData(dispatch);
}, [false]);
return <>{children}</>;
};
class MyApp extends App {
render() {
const { Component, router, pageProps } = this.props;
return (
<>
<AppProvider>
<AppWrapper>
<MainLayout pathname={router.pathname}>
<Component {...pageProps} />
</MainLayout>
</AppWrapper>
</AppProvider>
</>
);
}
}
// loadInitialData.js
import {
SET_DIETS_LIST,
UPDATE_FILTERS_FROM_CACHE,
} from "Store/types";
import fetch from "isomorphic-unfetch";
export default dispatch => {
const ls = JSON.parse(localStorage.getItem("filters"));
if (ls) {
const localStorageState = {
diet: {
list: ls.diet.list || [],
selected: ls.diet.selected || [],
},
...
};
dispatch({
type: UPDATE_FILTERS_FROM_CACHE,
payload: { filters: localStorageState },
});
}
if (!ls || !ls.diet.list.length) {
fetch(`${process.env.API_URL}/diets`)
.then(r => r.json())
.then(data => {
dispatch({ type: SET_DIETS_LIST, payload: { data[0] } });
});
}
...
};
It seems this filter is located on headermenu or sidebar menu?
If that is the case, I would suggest (an option other than _app.js) putting the API caller inside header/ sidebar component, and call the header/sidebar component on layout/ pages component.
Therefore, you will get the same behavior as what you've described (not invoking SSR on every pages and static optimization is still working because the concept is similar with the _app.js (just put it inside a structure).
I am new to react. I have created a news component that consumes a json url then spits out some news articles. In the client side UI if the clients changes the json url it will update without refreshing the page using this code:
componentDidUpdate(prevProps) {
if (prevProps.jsonUrl !== this.props.jsonUrl) {
this.getPosts();
}
}
However I also need the the news feed to update reactively if the postCount: this.props.postCount is changed in the client side UI. The post count is used in the render method below to choose how many posts to display.
posts
.slice(0, postCount)
.map(post => {
// Variables to use
let { id, name, summary, url, imgUrl} = post;
// Stripping html tags from summary
//let strippedSummary = summary.replace(/(<([^>]+)>)/ig,"");
// What we actually render
return (
<div key={id} className={ styles.post}>
<p>{name}</p>
{/* <p>{summary}</p> */}
<a href={url}>{url}</a>
<img className={ styles.postImage} src={imgUrl} />
</div>
);
})
Any help is much appreciated! - I was thinking something like this inside componentDidUpdate:
if (prevProps.postCount !== this.props.postCount) {
this.setState( this.state.postCount; );
}
EDIT:
I am now using the postCount from the props instead of a state and it updates instantly! :D
// Grabbing objects to use from state
const { posts, isLoading } = this.state;
const { postCount } = this.props;
The components are going to react automatically to the changes in their props, so there's no need to transfer any props to a state. In this case, if postCount is a prop, when it changes it should affect the piece of code that you shared to render the component. However, I don't know if posts is part of the state, in your case it should be and your method getPosts should setState with the new posts.