Navigate.push action rendering the page multiple times - react-native

I am working on React Native Application. In my sidebar (Drawer) menu to open the categories I used
const pushAction = StackActions.push({
routeName: 'CategoryList',
params: {
catId: 9,
},
});
this.props.navigation.dispatch(pushAction);
This logic is working fine. But, when I click on categories in drawer on first click page is rendered only minimum times that's needed. But, again when I click on drawer menu category page rendered increased by two times every time. Due to this multiple loaders are showing up. I cannot be able to find out the best solution for this problem yet.

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.

Populate Stack with multiple screens in React Navigation

After Google for weeks, I'm still yet to find a sensible approach to the following with React Navigation:
I have a Stack with several screens, the app consists of a todo list, of items...
The screens are:
Lists (show all of them)
List (gone into a specific list)
Items (show all items in the list)
Item (adding or editing an item)
When I receive a URL being shared into my app, I want to navigate to the Item screen, but have all the previous screens in place too, for when the user hits back.
This isn't a simple deeplink scenario. The url being shared could be any arbitrary text, not a specific url related to my app. (It's the url someone wants to put on their list).
Is there any way to do this from my App container? I currently try this, but I'm not sure if it's correct/sane.
this.navigationRef.current?.reset({
index: 0,
routes: [ { name: 'Lists' }, { name: 'List' }, { name: 'Items'}, { name: 'Item' } ],
});
This seems to be unmounting screens if they are already mounted, ie, if the user is already on the Item screen.
I found the "initial" param on navigate work well for another case, when I wanted the screen to be the 2nd one in the stack (and the default was used as the initial screen), but there doesn't seem to be a way to go "deeper".
My stack is also one of 3 tabs, if that makes things easier/worse.

react-navigation: have the same state as a previous route, but act as a push?

Say I have Screen 1 and Screen 2. I navigate from Screen 1 to Screen 2 by pushing. So Screen 1 is still there, and its state is unchanged. But, I want to be able to navigate to a third screen, Screen 3, that has EXACTLY the same state as Screen 1 (a copy), but acts like a push.
Pushing from Screen 1 to Screen 2 gives me a card transition, but I basically want to push from Screen 2 to Screen 3 (another card transition), but have Screen 3 to have the same state as Screen 1, like scrolling is the same, data is the same, etc.
EDIT: Unfortunately, I cannot upload any gifs for clarification because many of them are too large. An example would be from the Reddit app: moving from the tabular view to viewing a post (where the tab view disappears), but then when you click on the subreddit's name, it takes you (with a push animation) to another tab view, but it's the same one.
EDIT 2: Here is a link of a screen recording of what I want.
So the first screen shows me scrolling down to a certain area. (tabular view) - Screen 1
Then there's a card navigation to a "View Post Screen", where you can see comments, etc. (no tabs) - Screen 2
Then I clicked on r/aww, which navigates with a card transition to the subreddit page. (tabular view) - Screen 3
The tab for Screen 3 is scrolled to the same place as I scrolled in Screen 1. This is what I meant by "same state".
Pushing another route of the same name doesn't work, because everything is set back to default, and would be bad for user experience. Going back wouldn't work because it would show a card "go back" transition from Screen 2 to Screen 3 - a card "push" transition is more natural.
In react-navigation you can navigate to unlimited - in theory - of same screen. If you wish to change the contents of the screen you can use navigation parameters. You can think instagram app for example.
User(me) -> Followers -> User(Alice) -> Followers -> User(Bob) -> Followers -> User(John)
const Navigator = createStackNavigator({
Followers: { screen: Followers },
User: { screen: Profile },
});
/...
this.props.navigation.navigate('User', { user: 'me' });
this.props.navigation.navigate('Followers', { user: 'me' });
this.props.navigation.navigate('User', { user: 'Alice' });
this.props.navigation.navigate('Followers', { user: 'Alice' });
this.props.navigation.navigate('User', { user: 'Bob' });
this.props.navigation.navigate('Followers', { user: 'Bob' });
this.props.navigation.navigate('User', { user: 'John' });
My understanding of your use case scenario is that you want to preserve the styling and structure of a component across different screens. I have two takeaways here:
If the two screens (Screen1 and Screen3) are exactly the same, then why would you want a duplicate in the first place? Why not show the Screen1 again to keep your stack size small? In your example: User is on Screen1 which is subreddit's homepage. Then they click on a post link which takes them to Screen2. Again, when user clicks on the link to subreddit's homepage, they are not opening a new screen, they are simply going back to the original Screen1.
If there are slight differences between the two screens, your could simply employ component re-use. Like, if there are two subreddits r/android and r/reactnative, you'll have a single component for both of them: <SubRedditComponent> but their data and styling would be different.

How to navigate to a different view that is not a child?

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.