why I change the navigator props execute twice handler? - react-native

here is my View to switch page
this.props.navigator.push({
component: QuestionDetail,
passProps: {
id: id,
enteredDetail: function() {
console.log(this)
}
}
});
// in QuestionDetail
render: function() {
this.props.enteredDetail();
....
in Xcode I saw this
RCTJSLog> {"navigator":{},"route":{"passProps":{"id":"1010000002652090"}},"id":"1010000002652090"}
RCTJSLog> {"navigator":{},"route":{"passProps":{"id":"1010000002652090"}},"id":"1010000002652090"}
it appear twice! why?

See this issue:
https://github.com/facebook/react-native/issues/151
in order for the react diffing algorithm to correctly reset properties
back to their default state, we create a single dummy "defaultView"
for every type of view

Related

After adding a route to vue3 router. addRoute(), why is the page menu not updated?

console.log(router.getRoutes()); The number of arrays is increased by 1, and the menu is not updated
console.log(router.getRoutes()); //This array is 29,
//add route
router.addRoute('xitong2', {
path: '/projectbrowsing/duolianjixitong/xitong2',//
component: () => import('#/views/xitong/systemTabShow.vue'),
name: 'Xitong2',
meta: {
title: t('routers.xtyc'),
query: {
systemId: 'a02'
}
}
})
console.log(router.getRoutes()); //This array is 30,
Prompt
Note that adding routes does not trigger new navigation. That is, the added route will not be displayed unless a new navigation is triggered
Does triggering a new navigation refer to a page jump or something?

When passing data from parent component to child component via props, the data appears to be undefined in the mounted hook of the child component

In my parent component:
<UsersList :current-room="current_room" />
In the child component:
export default {
props: {
currentRoom: Object
},
data () {
return {
users: []
}
},
mounted () {
this.$nextTick( async () => {
console.log(this.currentRoom) // this, weirdly, has the data I expect, and id is set to 1
let url = `${process.env.VUE_APP_API_URL}/chat_room/${this.currentRoom.id}/users`
console.log(url) // the result: /api/chat_room/undefined/users
let response = await this.axios.get(url)
this.users = response.data
})
},
}
When I look at the page using vue-devtools, I can see the data appears:
I've run into this issue in the past – as have many others. For whatever reason, you can't rely on props being available in the component's mounted handler. I think it has to do with the point at which mounted() is called within Vue's lifecycle.
I solved my problem by watching the prop and moving my logic from mounted to the watch handler. In your case, you could watch the currentRoom property, and make your api call in the handler:
export default {
props: {
currentRoom: Object
},
data() {
return {
users: []
}
},
watch: {
currentRoom(room) {
this.$nextTick(async() => {
let url = `${process.env.VUE_APP_API_URL}/chat_room/${room.id}/users`
let response = await this.axios.get(url)
this.users = response.data
})
}
},
}
I don't think you really need to use $nextTick() here, but I left it as you had it. You could try taking that out to simplify the code.
By the way, the reason console.log(this.currentRoom); shows you the room ID is because when you pass an object to console.log(), it binds to that object until it is read. So even though the room ID is not available when console.log() is called, it becomes available before you see the result in the console.

Getting current title of page and updating from nested component and updating in Vuejs 2

I have a component called Notificiations.vue that is imported and used in the same parent component where my router-view is also being used.
Very basic example - my App.vue is something like:
<Template>
<Notifications></Notifications>
<router-view></router-view>
</Template>
Most of the individual pages that can be accessed with the router have a component that sets the page title like below:
metaInfo: function () {
return {
title: 'Leads - ' + this.view
}
}
What I'm wanting to do with Notifications.vue is whenever a new notification comes in, get the current title of the browser tab and just add in a (1) (or whatever number) to the front of it. I tried using regular Document.title to get the current title, but that is always returning undefined. What is another way I could do this?
I am assuming you have a data object in your notifications component.
A simplified version of Notification.vue
new Vue({
data: {
notifications: []
},
watch: {
notifications (current, previous) {
document.title = '(' + current.length + ')' + document.title.replace(/ *\([^)]*\) */g, "");
}
}
})
What we are doing here is watching the notifications object for changes. If it changes we are prepending the number of notification to the title of the document.
document.title.replace(/ *\([^)]*\) */g, "") this part is removing the current count of notifications prior to being updated with the new count.
Limitations to this approach:
If there are other (words) in parentheses in the title they will get stripped.
If the count of notifications is ZERO it will display (0) Title if the count is 1234 it will show (1234) Title. You may want to put some more checks in place to not show ZERO and perhaps do 9+ if length is > 9
An alternative approach would be to use Vuex to manage state.
const store = new Vuex.Store({
state: {
notifications: []
},
mutations: {
load (state, notifications) {
state.notifications = notifications
}
},
actions: {
load (context) {
Vue.$http.get('/notifications').then(response = {
context.commit('load', response.data);
})
}
}
});
// Notifications.vue
new Vue({
mounted () {
// you will want to add more than just an interval. You will want to keep track of this and perhaps stop it if, for example, the user logs out.
setInterval(function () {
store.dispatch('load');
}.bind(this), 1000)
}
});
// Add to your router
metaInfo: function () {
return {
title: '(' + store.state.notifications + ')' + 'Leads - ' + this.view
}
}
This was a quick example of how using Vuex would solve this problem. This is not tested and is for educational purposes only. Read more at https://vuex.vuejs.org/guide/

Vuejs 'beforeunload' event not triggered as expected

I have registered 'beforeunload' event on created hook of the component used by routes of vue router.
I want to call this event handler in order to remove user on browser tab close or browser tab refresh or browser close.
On ComponentA
created (){
window.addEventListener('beforeunload', () => {
this.removeUser()
return null
})
}
Smilarly on ComponentB
created (){
window.addEventListener('beforeunload', () => {
this.removeUser()
return null
})
}
And my router.js
{
path: '/staff/call/:session_key',
name: 'Staff Call',
component: ComponentA,
meta: {auth: true}
},
{
path: '/consumer/call/:session_key',
name: 'Consumer Call',
component: ComponentB
},
Here 'beforeunload' event handler is triggered randomly. That is sometimes it get triggered and sometimes not. I count find any pattern when it is triggered and when it is not.
What am I missing here?
Edit
I'd guess the most likely culprit then is exactly what #PatrickSteele said. From MDN:
Note: To combat unwanted pop-ups, some browsers don't display prompts
created in beforeunload event handlers unless the page has been
interacted with; some don't display them at all. For a list of
specific browsers, see the Browser_compatibility section.
I'd say it's likely you're seeing inconsistent behavior because you are sometimes not interacting with the page.
This may be a syntax error. created should be a method
created () {
window.addEventListener('beforeunload', this.removeUser)
},
methods: {
removeUser () {
//remove user here
}
}
A fiddle working: https://jsfiddle.net/e6m6t4kd/3/
It's work for me. while do something before reload or close in
vue.js
created() {
window.onbeforeunload = function(){
return "handle your events or msgs here";
}
}
I had to do some fiddling on the above examples, I believe this is the most robust solution:
let app1 = new Vue({
delimiters: ['[[', ']]'],
el: '#app',
data: {
dirty_form: true,
},
created () {
console.log('created')
window.addEventListener('beforeunload', this.confirm_leaving)
},
methods: {
confirm_leaving (evt) {
if (this.dirty_form) {
const unsaved_changes_warning = "You have unsaved changes. Are you sure you wish to leave?";
evt.returnValue = unsaved_changes_warning;
return unsaved_changes_warning;
};
};
},
});
If you want detect page refresh/change in Vue whenever you press F5 or Ctrl + R, You may need to use Navigation Timing API.
The PerformanceNavigation.type, will tell you how the page was accessed.
created() {
// does the browser support the Navigation Timing API?
if (window.performance) {
console.info("window.performance is supported");
}
// do something based on the navigation type...
if(performance.navigation.type === 1) {
console.info("TYPE_RELOAD");
this.removeUser();
}
}
Not sure why none of the above were fully working for me in vue 3 composition api. Abdullah's answer partially works but he left out how to remove the listener.
setup() {
const doSomething = (e) => {
// do stuff here
return true
}
onBeforeMount(() => {
window.onbeforeunload = handleLeaveWithoutSaving
})
onUnmounted(() => {
window.onbeforeunload = null
})
}

Run method before route

I have a login modal that I activate by setting .is-active to it. For this, I have a method like this:
methods: {
toggleModal: function (event) {
this.isActive = !this.isActive
}
}
that I run onclick. Depending on the boolean value of isActive, my modal gets the class .is-active.
Thing is, in my modal I have a button that takes the user to a new view which means it's rendering a new component, with this code:
<router-link class="control" #click="toggleModal()" to="/register">
As you can see, it's routing to /register. Before doing this, I need to run toggleModal() so that the modal gets closed. Right now it's routing without running the method which means that the new view has my modal overlay which is... not optimal.
Is there any good way to do this in Vue? Could I maybe create a method, that first calls toggleModal(), and then routes from the method?
Thanks.
I would define a method that calls toggleModal first, then navigates. Like so:
methods: {
navigateAway () {
this.isActive = !this.isActive
this.$router.push('/register')
}
}
You don't need the event argument unless you intend on capturing more data from the event or event target. You could also wrap the router push in a setTimeout if you so desire, for perhaps cleaner looking view changes.
methods: {
navigateAway () {
let vm = this
vm.isActive = !vm.isActive
setTimeout(function () {
vm.$router.push('/register')
}, 50)
}
}
Of course, there are hooks that you can use from vue-router that make this easy. Example (assuming you're using single file components and Vue.js 2.x):
export default {
data () {
return {
isActive: false
}
},
beforeRouteLeave (to, from, next) {
this.isActive = false // I assume that you would not want to leave it open upon navigating away
next()
}
}
Link to vue router hooks: https://router.vuejs.org/en/advanced/navigation-guards.html