setOnNavigatorEvent callback not fired when switching tabs - javascript

I am working on an app powered by react-native#0.55.4 and react-native-navigation#1.1.474.
Initially, there is only a login screen (using Navigation.startSingleScreenApp). When the user logs in I call Navigation.startTabBasedApp (one of the tab components is my NavScreen). Whenever the user changes to another tab the root of the tab's stack is supposed to be displayed so I tried something like this:
class NavScreen extends React.PureComponent {
constructor(props) {
super(props)
this.props.navigator.setOnNavigatorEvent(this.toRootOnTabSelect.bind(this))
}
toRootOnTabSelect(event) {
const {id} = event
if (["bottomTabSelected", "bottomTabReselected"].includes(id)) {
this.props.navigator.popToRoot({
animated: true,
animationType: "fade",
})
}
}
render() {
return <Text>Whatever...</Text>
}
}
But for some reason my toRootOnTabSelect event handler method is not being called when I change tabs (by clicking on them - not by calling the switchToTab API method).
There are multiple posts I found online (i.e. https://stackoverflow.com/a/51159091/6928824, https://github.com/wix/react-native-navigation/issues/648) that indicate that it should work so I don't know what I'm missing. :/
Any help is greatly appreciated! :)

One of the reasons that can cause that is using setOnNavigatorEvent in conjuction with addOnNavigatorEvent, if you have a screen wrapper component that implement addOnNavigatorEvent your current listener will not work.
As mentioned in documentation
setOnNavigatorEvent Can not be used in conjuction with addOnNavigatorEvent
Also
Bear in mind that you can't use both addOnNavigatorEvent and setOnNavigatorEvent. addOnNavigatorEvent returns a function, that once called will remove the registered handler.
I would suggest trying addOnNavigatorEvent instead of setOnNavigatorEvent

This seems to be a bug in react-native-navigation#^1.1.474 (note the caret): see my issue on GitHub.
An according pull request has been opened but not yet merged. :(

Related

Assigning onclick on button that is outside of current react app (php) gives unexpected behaviour

We are builidng react components for a php app, it doesn't really have much intergration but there's a php save button that is should listen for and save onclick. But there's a catch, for some reason most of functions i'm trying to use are just not getting ran. I'm working with react-form-hook and trying to run the onsubmit() with my function inside.
handleSubmit((data) => {
handleSaveOrderProducts(data, selectedProductsRef.current);
});
To assign this function to the button i have to listen for the button clicks with id, i tried different approaches but none seems to fix the problem. Ways to listen i tried:
Using https://www.npmjs.com/package/react-detect-click-outside#features lib
const productInformationContainerRef = useDetectClickOutside({
onTriggered(event) {
// #ts-ignore
if (event.target?.id === "save-order-button") {
console.log("save order button");
handleSubmit((data) => {
handleSaveOrderProducts(data, selectedProductsRef.current);
});
}
},
});
What i get in result? The callback itself runs, but the function (handlesubmit) is not. Even if i put a console.log in callback with data there's just nothing displayed.
Different approaches from this stackoverflow thread, mostly everything related to functional react Detect click outside React component .
If you had experience in something like this or have any theoretical knowledge please comment :)strong text

Google Optimize JS API on SPA

I have a problem with the Google Optimize JS API on my SPA, the callback is only run on page reload but when navigating back and forward to the specific page, the callback isn't being run. Here's the scenario:
1) I reload the application and go to my page having an active A/B-test: mysite.com/testpage
2) The folowing code is being run:
gtag('event', 'optimize.callback', {
name: experimentId,
callback: useVariant,
});
and useVariant(variantId) is being called setting the correct variantId.
3) I click or navigate away from the page and then click/navigate back to mysite.com/testpage, this is a SPA mind you.
4) The gtag() code is being re-called, BUT the callback useVariant() isn't being run.
5) WTF.
6) If I reload the page, using CMD+R (yes, I'm using Mac), the code runs as in point 1) And everything is ok.
I have tried the following:
A. Resetting the datalayer using google_tag_manager['xxx'].datalayer.reset()
B. Re-pushing the event with undefined callback gtag('event', 'optimize.callback', undefined)
C. Resetting datalayer = [] manually
D. Removing the optimize.callback event from the datalayer
E. Any combination of above (which gives a bit interesting results sometime)
So the question is in point 5: WTF? Any suggestions/thoughts are greatly appreciated.
Thanks!
I finally solved it! Here's how to fix it, maybe this will help someone else.
The problem is the callback function useVariant. I had defined it as so useVariant(variantId) when apperantly (thanks Google documentation for being clear on this, NOT) it has to be useVariant(variantId, experimentId)
So by changing the callback function to take two arguments the code will work.
For React, you can implement Activation Events in your top-level component's componentDidMount and componentDidUpdate lifecycle methods to run the experiment codes immediately after the component mounts or updates on the page.
Your code may look like this:
import React from 'react';
export default class App extends React.Component {
componentDidMount() {
dataLayer.push({ 'event': 'optimize.activate' });
}
componentDidUpdate() {
dataLayer.push({ 'event': 'optimize.activate' });
}
render() {
// experiment code goes here...
}
}
SPA tests on Google optimize

React arrow function produced different debugging experience

I am currently working on a react UI project, and involves lots of debugging with browsers. The strange thing is that when I do not use arrow function on button click event, the browser debugger shows this.state as undefined when mouse over it. When I use arrow function
{() => this.alert()}
the debugger console shows the correct state when mouse over and both these logging correct output when I do console.log(). Also I tested this behavior in Chrome and it is same as FF.
Any one has any idea will be much appreciated, since this brings really big issue when debugging without seeing the state, and it is not realistic to console.log for every state change.
Could not show this.state when mouse over it
Can show this.state when mouse over
Please note that when you do:
somehandler = () => { ... }
in your component, it's already bound to the component instance, i.e. this = your component instance, so you don't need to do onClick={() => this.somehandler() }, which binds it yet again to the component instance.
Stick to the former and you'll be good to go!
Edit: I now realize that it's actually onClick={this.somehandler} that's causing you console.log grief. It may be related to either how the Babel transform or the Dev Tools are implemented... You can try a classic bind like this, to see if it helps:
constructor() {
this.somehandler = this.somehandler.bind(this);
}
somehandler() {
// ....
}
render() {
return <button onClick={this.somehandler}>...</button>
}

BackAndroid not unmounting - React Native (Android)

I am working with React Native 0.29 with Android. For a particular view/activity/screen of my app, I want to add an event listener for BackAndroid button, which is available with react native. I already have a global BackAndroid event listener added to my app (in my index.android.js file) which pop out any view from the stack if it's not the main screen.
The event listener is activated with componentDidMount() lifecycle method and it works. It override the global one and works as expected. Now the problem is, it doesn't get removed when componentWillUnmount() lifecycle method get fired. So when back from that particular screen, the event listener still remains and cause trouble. Here is what I did:
componentDidMount() {
BackAndroid.addEventListener('backBtnPressed', this._handleBackBtnPress.bind(this))
}
componentWillUnmount() {
BackAndroid.removeEventListener('backBtnPressed', this._handleBackBtnPress.bind(this))
}
I don't understand why it's not working. Please help me to understand why it's not working and what should I do to solve this issue.
I spend hours to solve this problem. I think it would help other developers if I share this.
The main problem here is with the .bind(this) statement, bind always returns a new function. So in this code, this._handleBackBtnPress.bind(this) are not same functions in addEventListener and removeEventListener. They are different functions referring different first-class objects. That's why removeEventListener is not removing the listener.
To solve this issue, we can add the following statement to our constructor method - this._handleBackBtnPress = this._handleBackBtnPress.bind(this) and remove .bind(this) from both addEventListener and removeEventListener. So our code will look something like this:
constructor(props) {
super(props)
this._handleBackBtnPress = this._handleBackBtnPress.bind(this)
}
componentDidMount() {
BackAndroid.addEventListener('backBtnPressed', this._handleBackBtnPress)
}
componentWillUnmount() {
BackAndroid.removeEventListener('backBtnPressed', this._handleBackBtnPress)
}
Now both of them will refer same function and will work as expected.

Stop loading a new route/component if current component has changes

I have a requirement where I need to check if the local state has changes before the user navigates to the next tab. sort of like handing a component abandonment. I have come up with 2 options as follows,
Achieve this via componentWillUnmount. If its a good practice is there a way to conditionally stop the component being unmounted?
Via the window. As stated in the following solution : https://groups.google.com/forum/#!topic/reactjs/z63RGG1l_0U
Any idea on this matter is greatly appreciated :)
In the react-router-redux router is part of the state, so you can experiment on that.
To do so you should take a look at RouterContext, which provides setRouteLeaveHook function.
OR
As far as I remember there's also second option. Router object on context contains listenBeforeLeavingRoute
this.context.router.listenBeforeLeavingRoute
But its basically the same thing if you look at the source code of react-router. But its accessible from different layers.
EDIT:also Route has onLeave hook, may be useful!
Hope it helps somehow.
Regards,
Mariusz
How does the user navigate to the next tab? When you are using a <Link> you could define an unsavedChanges flag in your state. Set this to true (via dispatching an action and having a reducer responsible for that action) whenever you think that the user must not leave the current tab.
class Foo extends React.Component {
handleClick(e) {
const { unsavedChanges } = this.props
if(unsavedChanges) {
e.preventDefault()
}
}
render() {
return (
<Link to='/nextTab' onClick={this.handleClick}>Bar</Link>
)
}
}
Of course you need to pass unsavedChanges to your components props.

Categories

Resources