How to navigate to a different view that is not a child? - react-native

I am new to react native and new to iOS (not programming) so please excuse me if this question is a simple one. I am trying to navigate from one view to another (with a transition), however they are not related so I do not need the back navigation. I actually do not have a navigation bar at all. When using the Navigator component it seems to not support this at all. I am not sure if there is a separate way to do this but I am not able to figure it out without implementing my own hack.
If I use the navigator component and keep pushing on the views then it just keeps them all in memory and I do not want that. I can transition from one view to another and then pop but I may end up going to the wrong view in that case. I can also replace the view but it seems that does not allow for transitions.
To give you a scenario think of it like this:
Application starts and loads a "Loading" screen.
When initial loading is complete it will then go to the "Login" screen.
There is a button on the "Login" screen to "Register" or "Retrieve Password".
If they click "Register" it will take them there with a button back to "Login".
If they click "Retrieve Password" it will take them to a page with buttons to go back to "Login" or "Register".
So by this example you can see that there is no way to pop because if you were on the login screen and went to the register screen and then wanted to go the retrieve password screen then pop just simply wouldn't work. I do not want any navigation controls on the screen I just want to be able to do a smooth transition between these screens.
Now I was able to find a way to do this but I had to add a method to the Navigator class and hack code in using some of there core methods which seems like its not a good idea at all but here is the code (note this is really just a hack to see if it would work):
Navigator.prototype.pushWithUnmount = function(route) {
var activeLength = this.state.presentedIndex + 1;
var activeStack = this.state.routeStack.slice(0, activeLength);
var activeAnimationConfigStack = this.state.sceneConfigStack.slice(0, activeLength);
var nextStack = activeStack.concat([route]);
var destIndex = nextStack.length - 1;
var nextAnimationConfigStack = activeAnimationConfigStack.concat([
this.props.configureScene(route),
]);
this._emitWillFocus(nextStack[destIndex]);
this.setState({
routeStack: nextStack,
sceneConfigStack: nextAnimationConfigStack,
}, () => {
this._enableScene(destIndex);
this._transitionTo(
destIndex,
null, // default velocity
null, // no spring jumping
() => {
this.replaceAtIndex(nextStack[destIndex], 0);
this.setState({
presentedIndex: 0,
});
}
);
});
}
By using the code provided above I am now able to do:
this.props.navigator.pushWithUnmount({ component: SomeComponent });
With this code the views are pushed onto the stack with a transition and the old views are unmounted when its finished.
Please tell me that I am doing something wrong and that there is a better way to do this?

The default router with React Native is pretty limited. I'd check out React Native Router Flux. We just switched to it a few weeks ago in our product and have really liked it. It does exactly what you want.

Related

React Navigation: How to share data between main screen and modals

Working on react native with react navigation, building an app.
There is a topic screen with a complex data object, and using React Navigation's Modal to show some parts of data, separately. The problem is when the user navigates to a modal screen, I have to pass the topic id as param and fetch topic data from the server once again, and because of the nature of data, users may open and close modals several times at a time.
Couldn't find a solution to share data from the parent screen to its modals, and I don't want to share data with State and Context. So is there another way to pass data to modal screen without using param or hooks?
Have you tried navigating to the modal like that :
navigation.navigate("YourModalName", {
topic: yourTopicObject,
},
});
You should be able to use the topic object in your modal like that :
class YourModalName extends Component {
componentDidMount() {
console.log(this.props.topic); // Accessing object in props.topic
}
}

React native - State won't update on navigation

I'm not sure if the title is informative enough, but I'll try explaining my need.
I have two stacks in my application. I have a floating component that appears everywhere in my app(No matter in which stack I am), that shows some items, and by clicking them it will navigate to a component that renders this item. The navigation code is:
navigation.navigate('Tabs', {
screen: 'Home',
params: { screen: 'Dish', params: { from: '', data: dish } },
})
Now the problem is, if I'm already inside the screen Dish that render this item, and then use the floating component to navigate to another item, my state isn't rerendering, and keeps its old values.
I've managed to solve this by changing the code to:
navigation.push('Dish', {from: '', data: dish})}
Which simply push the component into the stack, though it made another problem; If I'm in my second stack (not the Tab one), then this doesn't work and won't navigate me anywhere, which make sense..
I also managed to solve this by navigating to the Tab stack and then pushing my component like this:
navigation.navigate('Tabs', {screen:'Home'})
navigation.push('Dish', {from: '', data: dish})}
This works, though I'm not sure if this is good practice. I was wondering if this is the correct way of achieving what I want.. Maybe I should just make my component rerender so the state changes? I tried to include as little code as I could, if anything else is needed I'll add it..
Thanks in advance!
It's not the best solution but you can use this :
componentDidUpdate(prevProps) {
if (this.props.route.params !== prevProps.route.params) {
//What you want to do
}
Using the DidUpdate only when the params change to avoid infinite loop.
Or looking after listener.

Reload previous screen in React Native

I've been using this way to refresh the previous screen when triggering a button from the current screen.
Screen A
constructor(props){
...
this.handleOnNavigateBack = this.handleOnNavigateBack.bind(this);
}
handleOnNavigateBack = (foo) => {
this.setState({ foo })
}
goToScreenB(){
this.props.navigation.navigate('ScreenB', {
onNavigateBack: this.handleOnNavigateBack
})
}
Screen B
goBackToScreenA(){
this.props.navigation.state.params.onNavigateBack(this.state.foo)
this.props.navigation.goBack()
}
I need to keep the function inside the parameters first so that it can be triggered when going back from screen b. But this is okay to use if it is from 1 screen to 1 screen. If I have a multi layer screen been navigated such as
from Screen A -> Screen B -> Screen C,
and go back
from Screen C -> Screen B -> Screen A then reload the function at Screen A, it will be troublesome.
Or maybe the situation is :
from Screen A -> Screen B, then from Screen B replace Screen C, then from Screen C -> Screen A then only reload the function at Screen A.
By doing so, I have to keep passing the function to reload when every navigation taken place. So I was wondering if there is a way to capture the screen entered and fire the specific function that you want like how Ionic did.
In Ionic case, there is an event called ionViewWillEnter where it helps to perform this reloading thing without going through with the function registration and all. You simply call out the function and that's it.
I've been trying to find a way that is working similar to this at React Native but I can't have it work around. I've heard about redux but never implement it as I still can't understand how it works so I'm trying not to use it first.
Based on your question you are looking for something similar to 'ionViewWillEnter' which will run a callback whenever a screen is focused.
Given that you are using React Navigation v5
The hook useFocusEffect serves the same purpose of running a function whenever a screen is focused this includes navigating back to a screen as well.
From the documentation
Sometimes we want to run side-effects when a screen is focused. A side
effect may involve things like adding an event listener, fetching
data, updating document title, etc. While this can be achieved using
focus and blur events, it's not very ergonomic.
The code would be something like this
useFocusEffect(
React.useCallback(() => {
alert('Screen focused')
// Do something when the screen is focused
return () => {
alert('Screen was unfocused');
// Do something when the screen is unfocused
// Useful for cleanup functions
};
}, [])
);
You can find the documentation here
https://reactnavigation.org/docs/use-focus-effect/
If your requirement is updating the data by passing something from screen B or C you can think of using Context which is easy to use or if you are planning to to use redux that is also a possible alternative.

React Native Navigation - startTabBasedApp - only load tab if clicked on

When using startTabBasedApp(params) in React Native Navigation (by Wix), all of the tabs load at once.
Is there a way to make it so that only the initial tab loads? And then only after clicking on another tab does that tab load?
I don't believe it's possible. See this comment for more information, it seems like they intended this behaviour.
A possible workaround is to use the following within the component:
onNavigatorEvent(event) {
switch (event.id) {
case "bottomTabSelected":
this.setState({ didPressTab: true })
break
case "willAppear":
ApplicationScreensManager.getInstance().registerCurrentActiveScreen(ApplicationScreens.WebViewContainerScreen)
}
break
}
}
while this case "bottomTabSelected": is fired only when selecting a tab, then you can set the state raising a flag and only then do whatever you intended on when pressing the tab.
good luck

React Native Navigator - limit the number of backs

I'm developing a complex App with many different screens with several forms. The users complain that from time to time it crashes and exits. I assume it can be from too many navigator.pushes so I'm trying to limit the number of backs, but I can't seem to get it working right.
I'm using immediatelyResetRouteStack which I call from Navigator onDidFocus prop.
The problem is, if I call it in Navigator onDidFocus prop it triggers a new render and the screen I'm viewing re-renders after about 1 second. I tried also in a generic function that I call whenever I do navigator.push but then it doesn't do the animation and it is very slow to change to a new screen.
Is there a better way to limit the number of backs?
My code:
_navigatorOnDidFocus(){
let routes=_navigator.getCurrentRoutes();
if (routes.length > 4){
let routesCopy = _.cloneDeep(routes);
let newRoutes = [
routesCopy[0],
routesCopy[2],
routesCopy[3],
routesCopy[4],
];
_navigator.immediatelyResetRouteStack(newRoutes);
}
}