Reactivity issue in IE browser - javascript

There is data that I receive from the server every 10 seconds in the Vuex repository:
From it I pull the data into the inside of the component through the calculated property:
computed: {
trackingStatusId: function () {
return this.$store.state.tracking.trackingServerData.tracking_status_id;
},
},
Next, I try to work with this data inside the method: And the method itself is attached to the styles in the html block(<i class="state" :style="getIndicatorWidth()"></i>)
methods: {
getIndicatorWidth: function () {
function widthCalculate(currentTrackingStatusId){
console.log(currentTrackingStatusId);
}
return widthCalculate(this.trackingStatusId);
},
},
Now we pay attention to this line in the above code:
console.log(currentTrackingStatusId);
If I get changed data from the server, then the value inside the console log changes reactively in all browsers except IE-11 (and below).
Getting data from the server is as follows:
(hung on a lifecycle hook mounted ())
methods: {
getTrackerIntervalData () {
setInterval(()=>myTimer(this), 5000);
function myTimer(th) {
return axios.get("https://seo-gmbh.eu/couriertracker/json/couriertracker_api.php?action=get_tracking_data&key_id=" + th.$route.params.tracking.toLowerCase() , {
})
.then(response => {
th.$store.commit('tracking/setTrackingServerData', response.data.data.tracking_data);
})
.catch(function (error) {
console.log(error);
});
}
},
}
Vuex repository:
const axios = require("axios");
export const state = () => ({
trackingServerData: null,
});
export const mutations = {
setTrackingServerData (state, trackingServerData) {
state.trackingServerData = trackingServerData;
},
};
I also duplicate the first request from middleware to be able to work with data when rendering a DOM tree:
export default function ({$axios, req, store, route}) {
if(req != undefined) {
store.commit('setIsFirstServerLoad', true);
}
else{
store.commit('setIsFirstServerLoad', false);
}
if(route.name == "language-tracker-tracking") {
return $axios.get("https://seo-gmbh.eu/couriertracker/json/couriertracker_api.php?action=get_tracking_data&key_id=" + route.params.tracking.toLowerCase(), {})
.then(response => {
store.commit('tracking/setTrackingServerData', response.data.data.tracking_data);
})
.catch(function (error) {
console.log(error);
});
}
}
As a result, if you look at the situation deeper, it turns out that the IE browser correctly calculates only the code received from middleware:
in the above screenshot, you can observe that the answer 8 - is correct - and the answer 3 is generally not clear where it came from.
(maybe even from the last page from the repository - although I had a separate repository for it there. I have no idea why this happens.)
Question:
Why data is not updated correctly in IE browsers?
How can this be fixed or is it possible to get around this problem - to implement everything differently?

the problem is here
getTrackerIntervalData () {
setInterval(()=>myTimer(this), 5000);
function myTimer(th) {
return axios.get("https://seo-gmbh.eu/couriertracker/json/couriertracker_api.php?action=get_tracking_data&key_id=" + th.$route.params.tracking.toLowerCase() , {
})
.then(response => {
th.$store.commit('tracking/setTrackingServerData', response.data.data.tracking_data);
})
.catch(function (error) {
console.log(error);
});
}
}
IE dosent support arrow function.
you should do it like so
getTrackerIntervalData () {
let vm=this;
setInterval(function(){myTimer(vm)}, 5000);
function myTimer(th) {
return axios.get("https://seo-gmbh.eu/couriertracker/json/couriertracker_api.php?action=get_tracking_data&key_id=" + th.$route.params.tracking.toLowerCase() , {
})
.then(response => {
th.$store.commit('tracking/setTrackingServerData', response.data.data.tracking_data);
})
.catch(function (error) {
console.log(error);
});
}
}
because vue use webpack. normally arrow function will be transform to normal function to support all browsers but maybe for some reason the setInterval with arrow function not transform. try it maybe that the problem

Related

Axios interceptor repeats previous loop and incrementally adds new loops per new request

In my react app I use an axios interceptor for my global error handling. Because I use React Context to show and handle my notifications I had to move the interceptor part to a functional component where I can use the React useContext hook.
const Interceptor = ({ children }) => {
const { addNotification } = useContext(NotificationContext);
apiClient.interceptors.request.use(
(config) => {
return config;
},
(error) => {
return Promise.reject(error);
}
);
apiClient.interceptors.response.use(
(response) => {
return response;
},
(error) => {
if (error.message === "Network Error") {
if (error?.response?.status === 504) {
addNotification("error", "Oops!", "gateway_timeout");
} else {
addNotification("error", "Oops!", "server_down");
}
} else {
if (error?.response?.config?.url !== "/me") {
addNotification("error", "Oops!", error.response.data.message);
}
}
return Promise.reject(error);
}
);
return <>{children}</>;
};
This works - errors are being caught and a notification is shown - for the first response with an error. The second time the first notification is shown again and two new notification are being made. The third time the previous three notifications are shown again and three new ones are being made and so on. The problem seems to come from the interceptor which is being ran incrementally (1,3,6,...)
Demo (hit the login button twice or more to see)
Problem
You are adding a new interceptor every render, but you only want to do it once.
Solution
Wrap your code in useEffect
useEffect(() => {
apiClient.interceptors.request.use(
// ...
)
apiClient.interceptors.response.use(
// ...
)
}, [])

How can I write a .then function with async/await, so that I can catch the response from axios (in seperate files and methods, in vue)

My target is to catch a respone, from a axios request, which works greate with .then, but I would like use async/await, since it is a new approach with lots of benefits.
(The update method is called multiple times)
How transform my saveEdit method (which gets a response form the update method) with async/await, so that I can catch the response from axios.
Method of my .vue file:
...
saveEdit (event, targetProperty, updateValue) {
this.update(this[updateValue])
.then((result) => {
if (result.status === 200) {
this.fetchData()
this.cancelEdit()
}
})
}
...
My function of my store module:
(api is a handler for axios, basicall axios. ...)
update ({ commit, rootGetters }, details) {
...
const requestUrl = `some adress`
return api
.patch(
requestUrl,
validatedDetails
)
.then(response => {
return response
})
.catch(error => {
return Promise.reject(error)
})
}
Other stackoverflow posts related to that problem, did answer my question, since in the examples are in one file and one method.
Thanks in advance
you can try someting like:
update ({ commit, rootGetters }, details) {
...
const requestUrl = `some adress`
return api.patch(
requestUrl,
validatedDetails
)
}
and :
async saveEdit (event, targetProperty, updateValue) {
try {
const result = await this.update(this[updateValue])
if (result.status === 200) {
this.fetchData()
this.cancelEdit()
}
} catch (error) {
// handle api call error
}
}

How to reset recaptcha on Success for signInWithPhoneNumber()

I am using Firebase.auth()signInWithPhoneNumber(number, appVerifier)
Everything is working as expected however I am trying to resolve the issue below:
Here is my implementation:
useEffect(() => {
window.recaptchaVerifier = new app.auth.RecaptchaVerifier("sendBtn", {
size: "invisible",
callback: function () {
onSend();
},
});
});
const onSend = (value) => {
const appVerifier = window.recaptchaVerifier;
const setMobile = "valid mobile..";
firebase
.auth()
.signInWithPhoneNumber(setMobile, appVerifier)
.then(function (confirmationResult) {
appVerifier.reset()
console.log(confirmationResult)
})
.catch(function (error) {
appVerifier.reset()
console.log(error);
});
};
How can I correctly handle Recaptcha? Without it being rendered multiple times. I'm looking to destroy it on Recaptcha on success, I have gone through the documentation here but clear() or reset() does not seem to work
You can provide a empty array of dependencies to useEffect to trigger only after initial render, more details in this Stack Overflow Answer.
Additionally it may be a good idea to add an if check to see if window.recaptchaVerifier is set (in case you have component using recaptcha anywhere else on your page), before trying to initialize a new RecaptchaVerifier.
useEffect(() => {
if (!window.recaptchaVerifier) {
window.recaptchaVerifier = new app.auth.RecaptchaVerifier('sendBtn', {
size: 'invisible',
callback: function () {
onSend();
}
});
}
}, []);
You have to provide the dependencies of your useEffect otherwise it will be executed each time the component render.
useEffect(() => {
// recaptcha
}, [])

Asynchronous ActionCreator in React Redux

I'm pretty new in React-Redux. Was working on an application. The thing is that I faced some issues with asynchronous execution of Redux actionCreator, may be.
Below is my component. Say, I want to call an actionCreator from componentDidMount() or from an onclick event listener.
class Dashboard extends PureComponent {
componentDidMount() {
this.props.getProductsAndPackages();
let something = [];
something = this.props.products;
}
....................................
}
Or , the function this.props.getProductsAndPackages(); can be an onClick event handler that does the same thing, context is the same. I'll ask my question after first explaining my code.
At the lower side of my Dashboard container:
Dashboard.propTypes = {
getProductsAndPackages: PropTypes.func.isRequired,
products: PropTypes.array.isRequired,
.......................
};
const mapStateToProps = (state) => {
return {
.....................
products: state.products.products,
...................
};
};
const mapDispatchToProps = (dispatch) => {
return {
getProductsAndPackages: () => dispatch(getProductsAndPackagesActionCreator()),
};
};
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Dashboard));
My actionCreator goes like:
export const getProductsAndPackagesActionCreator = () => {
return (dispatch) => {
dispatch(productsIsLoading(true));
let url = 'xyz';
if(!!localStorage.getItem('_token')) {
const local_token = localStorage.getItem('_token');
const fullToken = 'Bearer '.concat(local_token);
axios.get(url, {headers: {Authorization: fullToken}})
.then(response => {
dispatch(productsIsLoading(false));
if (response.data.statusCode === 200) {
dispatch(productsFetched(true));
dispatch(products(response.data.data));
} else {
dispatch(productsFetched(false));
dispatch(productsErrors(response.data.message));
}
})
.catch(error => {
});
} else {
axios.get(url)
.then(response => {
dispatch(productsIsLoading(false));
if (response.data.statusCode === 200) {
dispatch(productsFetched(true));
dispatch(products(response.data.data));
} else {
dispatch(productsFetched(false));
dispatch(productsErrors(response.data.message));
}
})
.catch(error => {
console.log(error);
dispatch(productsIsLoading(false));
dispatch(productsErrors(error.message));
});
}
};
};
Now, I want my getProductsAndPackagesActionCreator() to return a Promise or anything that would allow my something variable to get the actual data returned from the server. Right now, by the time I'm getting actual data, the line something=this.props.products has already been executed and I get back the initialValue that was set for products.
I know, whenever I'll receive the populated products, component will re-render, but that does not help my decision making.
I'm using redux-thunk, by the way.
What should I do now ? Sorry for such a long post.
Actually I wanted getProductsAndPackagesActionCreator() to return a promise, which was pretty straightforward, to be honest. I figured out that if you just return the axios.get() or axios.post(), it will return a promise. So, the modified code looked like below:
export const getProductsAndPackagesActionCreator = () => {
return (dispatch) => {
dispatch(productsIsLoading(true));
let url = 'xyz';
if(!!localStorage.getItem('_token')) {
return axios.get(url, {headers: {Authorization: fullToken}})
.then(response => {
............
............
})
.catch(error => {
});
} else {
return axios.get(url)
.then(response => {
...........
...........
})
.catch(error => {
console.log(error);
});
}
};
};
And then, I could do something like below in componentDidMount() or on any onClick event:
this.props.getProductsAndPackages().then(() => {
this.setState({
...this.state,
clicked_product: this.props.product_by_id
}, () => {
//do other stuffs
});
});
Feel free to let me know if there's any issue.
I think you are close to getting what you want. First of all, you should understand that redux actions and react actions like setState are asynchronous, so you have to apply your logic keeping this in mind. I'm going to explain what i think in some points:
You have called the action creator in the correct place componentDidMount, also you can call this action in any onClick if you want.
As soon as you dispatch the action you are changing your redux state setting loading true I suppose. So now you can access this property in your render function, so you can render a Loader until your api call finishes.
When your ajax function finishes, with an error or not, I suppose you are setting loading to false and updating your products data, so you can render now your loaded products in your dashboard.
Are you sure that you have to compare your empty products array with the received data? Maybe you can check in your render function if (!this.props.products.length) return null, when you load your page you will see a loader function and later your dashboard with the products.
If you really need to compare previous products with received products componentDidUpdate is your method. In this method, you can access your previous props and compare with actual props, be careful comparing arrays, remember [] === [] is false. Maybe you can compare the length, something like
componentDidUpdate(prevProps){
if(prevProps.products.length !=== this.props.products.lenth){
doSomething()
}
}
Just to say that componentDidUpdate is executed after render, so be careful with your code to no-execute extra renderings.
Hope it helps, if you dont understand anyting just tell me :)

How to get rid of promise when getting json data

I'm trying to simply get data from a weather API. Here's my api link to get http://api.wunderground.com/api/5b81d144ae2d1942/conditions/q/46.838260,-71.293689.json
In my api.js file, i have this basic function :
const baseUrl = `http://api.wunderground.com/api/5b81d144ae2d1942/conditions/q`;
export const getCurrent = (lat,lon) => {
return fetch(`${baseUrl}/${lon},${lat}.json`)
.then((response) => response.json())
.then((json) => {
console.log(json.current_observation.weather)
return json.current_observation
})
.catch(() => {
console.error('unable to fetch tasks')
})
}
Notice the console.log, in this function i'm able to fetch the json data, i got the value i want.
Now, in my Vue, i call this function this way :
export default {
data: () => ({
current: []
}),
created: function () {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(this.showPosition);
}
},
methods: {
showPosition(position) {
const data = api.getCurrent(position.coords.longitude,position.coords.latitude);
this.current = data;
console.log(this.current);
}
}
}
For some reason, the console.log in here gives me this :
PromiseĀ {<pending>}__proto__:
Promise[[PromiseStatus]]: "resolved"
[[PromiseValue]]: Object
I don't know what's going on but i can't access the data. I searched on the net, a lot of pages talk about this, but couldn't find the exact solution, only long texts ...
Is there a solution for this (code please)
Thanks a lot.
To "get rid" of the Promise and access it's data, use .then() in .getCurrent()'s result, just like you are when using fetch():
methods: {
showPosition(position) {
api.getCurrent(position.coords.longitude,position.coords.latitude)
.then((data) => {
this.current = data;
console.log(this.current);
}
}
}
Alternatively, you could declare showPosition as async, and use await:
methods: {
showPosition: async function(position) {
const data = await api.getCurrent(position.coords.longitude,position.coords.latitude);
this.current = data;
console.log(this.current);
}
}
Just keep in mind the results of both executions will be processed asynchronously, meaning this.current will not have the value of data right away.

Categories

Resources