The "Watch" event isn't firing in a Vuex Store. This is pretty basic. It's no more complicated than the example in the help. Does Watch not work in a store?
(All the answers I could find were regarding Angular.)
-Thanks
in store.js
state: {
warningSwitch : false,
}
(...)
watch: {
warningSwitch: function (newState, oldState) {
/* console.log(" Triggered "); */
if(newState){
this.showWarningNotice({title: "Oops", text:"Connection Lost"});
return;
} else {
this.showInfoNotice({title: "Whew...", text:"Connected"});
}
}
},
(...)
mutations: {
setWarningOn(state) {
state.warningSwitchCounter++;
state.warningSwitch = true;
},
}
There is no such thing as a "watch" in a store, as Bert alluded to in the comments.
There are two different approaches that you can take to simulate this behavior, depending on your needs and your architecture.
One, you can watch the store's getters in your component and respond to it there:
watch: {
`$store.getters.warningSwitch`: function (newState, oldState) {
/* console.log(" Triggered "); */
if(newState){
this.showWarningNotice({title: "Oops", text:"Connection Lost"});
return;
} else {
this.showInfoNotice({title: "Whew...", text:"Connected"});
}
}
},
Two, you can actually dispatch additional behavior in the action of the store:
actions: {
setWarningOn: function(context, newState) {
// broadcast event here or commit another mutation
}
}
Related
i'm use Vue 2 and i wanna detect sockjs errors and show notification.
(Like 'Connection lost','connection timeout' etc. )
I have no idea how to do it
The browser has a built in method called navigator.onLine, which returns either true or false. Now to watch for connection changes you can add an event listener on window,
window.addEventListener('offline', (e) => { console.log('offline'); });
window.addEventListener('online', (e) => { console.log('online'); });
You can incorporate this into a Vue component with something like:
export default {
data() {
return {
online: navigator.onLine
};
},
mounted() {
window.addEventListener("online", this.onchange);
window.addEventListener("offline", this.onchange);
this.onchange();
},
beforeDestroy() {
window.removeEventListener("online", this.onchange);
window.removeEventListener("offline", this.onchange);
},
methods: {
onchange() {
this.online = navigator.onLine;
this.$emit(this.online ? "online" : "offline");
}
}
};
And then use v-if="!online" to selectively render you're offline banner.
Alternatively, take a look at: v-offline, it instead works by pinging an endpoint, which has the advantage of being able to detect when the user is online but with very poor internet connection (loading), however is an overall less efficient approach.
import offline from 'v-offline';
export default {
components: {
offline
},
methods: {
handleConnectivityChange(status) {
console.log(status);
}
}
}
For most sock.js methods, you can get this information from the Event parameter returned by the callback. But for detecting network connection, and other common tasks, it's usually more robust to do natively, as outlined above.
NOTE: Marking Michael Cox's answer below correct, but you should read the discussion underneath the answer as well, which illustrates that componentDidMount is apparently called BEFORE browser render.
I have a ReactJS app, and when the user clicks a button, I'd like to change the button text to "Working..." so that the user can see that the app is working on their request. AFTER the updated button has been rendered, then and only then, do I want to actually start the operation.
My first attempt was to use componentDidUpdate. AFAIK, it shouldn't be called until AFTER rendering. But here is my attempt, and you can see the button never changes (open up console to help it take long enough). https://jsfiddle.net/joepinion/0s78e2oj/6/
class LongOperationButton extends React.Component {
constructor(props) {
super(props);
this.state = {
opInProgress: false,
};
}
render() {
let btnTxt = "Start Operation";
if(this.state.opInProgress) {
btnTxt = "Working...";
}
return(
<button
onClick={()=>this.startOp()}
>
{btnTxt}
</button>
);
}
startOp() {
if(!this.state.opInProgress) {
this.setState({opInProgress: true});
}
}
componentDidUpdate() {
if(this.state.opInProgress) {
this.props.op();
this.setState({opInProgress: false});
}
}
}
function takeALongTime() {
for(let i=1;i<=2000;i++) {
console.log(i);
}
}
ReactDOM.render(
<LongOperationButton op={takeALongTime} />,
document.getElementById('container')
);
Next attempt I tried using the callback of setState. Seems less correct because it bypasses componentShouldUpdate, but worth a shot: https://jsfiddle.net/joepinion/mj0e7gdk/14/
Same result, the button doesn't update.
What am I missing here???
laurent's comment is dead on. You're code is executing this.props.op, then immediately updating the state. TakeALongTime needs to signal when it's done.
function takeALongTime() {
return new Promise(resolve => {
setTimeout(resolve, 2000);
});
}
and componentDidUpdate needs to wait until op is done before setting the state.
componentDidUpdate() {
if(this.state.opInProgress) {
this.props.op().then(() => {
this.setState({opInProgress: false});
});
}
}
(This is going off of your first example.)
You could also do this with callbacks.
function takeALongTime(cb) {
setTimeout(cb, 1000);
}
componentDidUpdate() {
if(this.state.opInProgress) {
var parentThis = this;
this.props.op(function() {
parentThis.setState({opInProgress: false});
});
}
}
I have a Vue component that is tracking when it is "dirty" (e.g. unsaved). I would like to warn the user before they browse away from the current form if they have unsaved data. In a typical web application you could use onbeforeunload. I've attempted to use it in mounted like this:
mounted: function(){
window.onbeforeunload = function() {
return self.form_dirty ? "If you leave this page you will lose your unsaved changes." : null;
}
}
However this doesn't work when using Vue Router. It will let you navigate down as many router links as you would like. As soon as you try to close the window or navigate to a real link, it will warn you.
Is there a way to replicate onbeforeunload in a Vue application for normal links as well as router links?
Use the beforeRouteLeave in-component guard along with the beforeunload event.
The leave guard is usually used to prevent the user from accidentally
leaving the route with unsaved edits. The navigation can be canceled
by calling next(false).
In your component definition do the following:
beforeRouteLeave (to, from, next) {
// If the form is dirty and the user did not confirm leave,
// prevent losing unsaved changes by canceling navigation
if (this.confirmStayInDirtyForm()){
next(false)
} else {
// Navigate to next view
next()
}
},
created() {
window.addEventListener('beforeunload', this.beforeWindowUnload)
},
beforeDestroy() {
window.removeEventListener('beforeunload', this.beforeWindowUnload)
},
methods: {
confirmLeave() {
return window.confirm('Do you really want to leave? you have unsaved changes!')
},
confirmStayInDirtyForm() {
return this.form_dirty && !this.confirmLeave()
},
beforeWindowUnload(e) {
if (this.confirmStayInDirtyForm()) {
// Cancel the event
e.preventDefault()
// Chrome requires returnValue to be set
e.returnValue = ''
}
},
},
The simplest solution to mimic this fully is as follow:
{
methods: {
beforeWindowUnload (e) {
if (this.form_dirty) {
e.preventDefault()
e.returnValue = ''
}
}
},
beforeRouteLeave (to, from, next) {
if (this.form_dirty) {
next(false)
window.location = to.path // this is the trick
} else {
next()
}
},
created () {
window.addEventListener('beforeunload', this.beforeWindowUnload)
},
beforeDestroy () {
window.removeEventListener('beforeunload', this.beforeWindowUnload)
}
}
I'd like to route the user to a certain screen, in case he is not connected to the internet.
I just can't detect if he is connected or not.
I tried this code, but did not work:
async componentWillMount()
{
if (!await NetInfo.isConnected)
{
this.props.navigation.navigate('Saved');
}
}
Any tested solution to suggest?
Try await NetInfo.isConnected.fetch()
ref : https://facebook.github.io/react-native/docs/netinfo.html#isconnected
You can check using NetInfo .
for that you have to add connectionChange event listener like this
componentDidMount() {
NetInfo.isConnected.addEventListener('connectionChange', this.handleConnectionChange.bind(this));
NetInfo.isConnected.fetch().done(
(isConnected) => { this.setState({ isConnected: isConnected }); }
);
and then remove the event listener in componentWillUnmount
componentWillUnmount() {
NetInfo.isConnected.removeEventListener('connectionChange', this.handleConnectionChange);
}
And finally the handler method for connection change. I am storing the status in device local storage you can do whatever you want.
handleConnectionChange = (isConnected) => {
if (isConnected) {
//ToastAndroid.show('Data sync in process...', ToastAndroid.SHORT);
AsyncStorage.getItem('offlineData')
.then((json) => JSON.parse(json))
.then((data) => {
console.log(JSON.stringify(data));
});
}
else { ToastAndroid.show('You are offline.', ToastAndroid.SHORT); }
this.setState({ isConnected: isConnected });
}
Don't forget to add NetInfo from react-native :)
Another solution to your case (one without using isConnected property) is to use the object returned from the event handler directly like that:
componentDidMount() {
NetInfo.addEventListener('connectionChange', this.handleNetworkChange);
}
componentWillUnmount() {
NetInfo.removeEventListener('connectionChange', this.handleNetworkChange);
}
handleNetworkChange = (info) => {
if (info.type === 'none') {
this.props.navigation.navigate('Saved');
}
};
According to NetInfo documentation:
connectionChange event fires when the network status changes. The argument to the event handler is an object with keys:
type: A ConnectionType (listed above)
effectiveType: An EffectiveConnectionType (listed above)
The connection type can be one of the following : none, wifi, cellular, unknown.
Ideally you can store this information to your redux store and the listener to a root component.
We had a weird bug when using isConnected similar to the one you mentioned #Gabriel Bleu but for us, the NetInfo.isConnected.fetch() returned false only when the Android device was awake after some period of inactivity.We used it to display offline warning for users, so the warning never left. I found this solution on a Spencer Carli's course and it seems to work better but depending on your needs, you might want to use isConnected combined with the above code.
This is a great example to check online or offline and even you can have connection change information too. Source
NetInfo.isConnected.fetch().then(isConnected => {
console.log('First, is ' + (isConnected ? 'online' : 'offline'));
});
function handleFirstConnectivityChange(isConnected) {
console.log('Then, is ' + (isConnected ? 'online' : 'offline'));
NetInfo.isConnected.removeEventListener(
'connectionChange',
handleFirstConnectivityChange
);
}
NetInfo.isConnected.addEventListener(
'connectionChange',
handleFirstConnectivityChange
);
There are two issues with your code currently.
In newer versions of react life-cycle method componentWillMount is deprecated.
Newer versions of react-native have extracted the NetInfo Module out of the core. Use #react-native-community/netinfo instead.
In order to achieve the desired behavior you should do something like this.
import NetInfo from "#react-native-community/netinfo";
class CheckConnection extends Component {
componentDidMount() {
NetInfo.fetch().then(state => {
handleConnectionState(state)
});
}
handleConnectionState(state) {
console.log("Connection type", state.type);
console.log("Is connected?", state.isConnected);
... your code to handle the lack of connection
}
}
I currently am using a function on a timed loop to check if a user goes offline:
created() {
setInterval(() => {
this.checkOnline();
}, 30000);
}
Method:
checkOnline(){
this.onLine = navigator.onLine ? true : false;
}
Is there a way I can detect this without using the timer?
Secondly...
I am trying to show an 1 second alert to tell the user they are back online. When this.onLine is true, the div should be hidden. When it is false it should also be hidden, but when it goes from false to true I want to show the div for a second or so and then hide it again. I have tried this with a settimeout and using a watcher but neither give the desired effect.
Edit:
So one method that gets me there is:
data() {
return {
onLine: navigator.onLine ? true : false,
}
}
then a watcher to show the back online message
watch: {
onLine: function (val) {
if(this.onLine === true){
this.showBackOnline = true;
setTimeout(()=>{ this.showBackOnline = false; }, 1000);
}
},
},
Is there a better way than a watcher to achieve this, other than using a dedicated notify plugin?
Is there a way I can detect this without using the timer? Yes. By using the online offline event.
Is there a better way than a watcher to achieve this, other than using a dedicated notify plugin? I think the watcher is the most suitable method here.
demo: https://jsfiddle.net/jacobgoh101/xz6e3705/4/
new Vue({
el: "#app",
data: {
onLine: navigator.onLine,
showBackOnline: false
},
methods: {
updateOnlineStatus(e) {
const {
type
} = e;
this.onLine = type === 'online';
}
},
watch: {
onLine(v) {
if (v) {
this.showBackOnline = true;
setTimeout(() => {
this.showBackOnline = false;
}, 1000);
}
}
},
mounted() {
window.addEventListener('online', this.updateOnlineStatus);
window.addEventListener('offline', this.updateOnlineStatus);
},
beforeDestroy() {
window.removeEventListener('online', this.updateOnlineStatus);
window.removeEventListener('offline', this.updateOnlineStatus);
}
})
you can handle this (update onLine status), just by set arrow function in event listener and set that varible true/false by listener.
window object has online/offline events and you don't need to watch for navigator.online value or set interval to check it. you can just set event listener for it and do something on callback method.
in below code i just change value of my 'isOnLine' variable value and in template show message by v-if directive to user about online/offline.
data() {
return {
isOnLine:navigator.onLine
}
},
mounted() {
window.addEventListener('online', ()=>{this.isOnLine=true});
window.addEventListener('offline', ()=>{this.isOnLine=false});
},