I often find myself wanting to "replace" a route i.e not adding to the history yet have the route change animate.
In iOS I would do something like this.
[navigationController popToRootViewControllerAnimated:NO];
[navigationController pushViewController:someOtherViewController animated:YES];
Maybe this is related to issue #1953 I think I would prefer an api where we could do
replace(route, animated);
push(route, animated);
Was thinking about the same.
If we can agree on an API I would gladly submit a PR implementing it.
What do you think, Eric?
I think the API looks reasonable, and we have actually experimented with something similar. We're working on some big changes to Navigator to get the route stack logic out, and to upgrade to the new Animated API. This should be pretty easy to add when we make those changes
Animated replace would've been awesome! I often find it useful to replace main view controller when using e.g. side menu. Unfortunately, I am quite limited currently, so waiting for the updates in this matter
I'm doing something like this now to skip a payment screen route that I really don't want in the stack:
var r = navigator.getCurrentRoutes();
var backCount = 1;
if (route.props.fromPayment)
backCount = 2;
navigator.popToRoute(r[r.length-1-backCount]);
It works, but I'd really rather not have this screen have the responsibility to know about the previous routes.
@miracle2k for this specific use case, take a look at our implementation https://github.com/este/native/blob/master/src/app/app.react.js#L105-L108. We are decorating Navigator with few handy methods and passing it down, this particular one works quite well.
For now maybe you could do a push and then immediatelyResetRouteStack once the transition completes
@ericvicenti cool. so immediatelyResetRouteStack in didfocus should do the trick?
Inspired by the suggestion from @grabbou, I am now using this workaround:
function betterNavigator(navigator) {
const navigation = {
pop: () => {
debugger;
const routes = navigator.getCurrentRoutes();
// Find the next route without replace mode
var target = null;
for (let i=routes.length-2; i>=0; i--) {
if (!routes[i].replace) {
target = routes[i];
break;
}
}
if (target)
navigator.popToRoute(target);
else
navigator.pop();
},
popToTop: navigator.popToTop,
popToRoute: navigator.popToRoute,
popBack: (index) => {
const routes = navigator.getCurrentRoutes();
navigator.popToRoute(routes[routes.length - index - 1]);
},
replaceAtIndex: navigator.replaceAtIndex,
push: navigator.push,
replace: (target) => {
debugger;
const routes = navigator.getCurrentRoutes();
routes[routes.length-1].replace = true;
navigator.push(target);
},
getCurrentRoutes: navigator.getCurrentRoutes
};
return navigation;
}
Note the overwrites of replace() and pop(). pop will skip all routes that have been replaced.
@miracle2k Although that allows replace to use the animations from push, I don't think it behaves the same. replace() will unmount the current component and then mount the new one immediately.
@miracle2k @fender I found this problem too, the view i want to replace was a view which using camera, and using this way the camera page cannot be unmounted and it is keep using the camera, which makes my App deny by App Store because of something like "using camera background". I am now using setTimeout to push new view back after poping current view.
Hope next official release can fix this.
I still find this problem as well, when I have sceneconfig set, it does not work for type replace even though it does for pop
Any ETA on this, @ericvicenti ?
Looks like following code works well, could it be used as workaround?
navigator.immediatelyResetRouteStack(navigator.getCurrentRoutes().splice(-1,1));
navigator.push(ROUTE);
@grabbou that is going to remove all prev routes i guess.
Few ideas:
1) One can push first and then popN routes without animating which gives the same effect as animated replace. The back button on pushed however needs to be handled correctly.
2) One can popN routes without animating and then push and the back button will be automatically adjusted correctly.
Crazy hack:
Navigator.prototype.popNWithoutAnimation = function(n) {
if (n === 0) {
return;
}
this.state.routeStack.splice(this.state.presentedIndex - n + 1, n);
this.state.sceneConfigStack.splice(this.state.sceneConfigStack - n + 1, n);
this.state.presentedIndex = this.state.presentedIndex - n;
this.state.transitionCb = null;
};
ExNavigator.prototype.popNWithoutAnimation = function(n) {
return this.__navigator.popNWithoutAnimation(n);
};
I try another way which execute normally.
this.props.navigator.immediatelyResetRouteStack(this.props.navigator.getCurrentRoutes().slice(0, -1));
hope make sense.
regards.
For now I use this workaround:
https://gist.github.com/vaukalak/40ed12eadf0f605a8ffc4584144f2fdd
then in my code I do:
if(!this.navigatorUtils) {
this.navigatorUtils = navigatorUtils(this.refs['navigator']);
}
this.navigatorUtils.resetToAnimated(props.screen);
not too elegant, but seems to work :)
I have tried all of these solutions above but all didn't work.
So I had to check the Navigator source code while coded on my own. Finally after solving some problems such as flashing and animating, not working at all, replacing without animation though I have written the code, I got the solution.
These are my code below. Most of them are copied from Navigator source code. What I did is just combine them in a right way. Um...of course, they still can be optimized. And they too much hard to understand. Trust me, I won't understand after tonight. But it works. It's enough.
BTW, my RN is 0.27.
Navigator.prototype.replaceWithAnimation = function (route) {
const activeLength = this.state.presentedIndex + 1;
const activeStack = this.state.routeStack.slice(0, activeLength);
const activeAnimationConfigStack = this.state.sceneConfigStack.slice(0, activeLength);
const nextStack = activeStack.concat([route]);
const destIndex = nextStack.length - 1;
const nextSceneConfig = this.props.configureScene(route, nextStack);
const nextAnimationConfigStack = activeAnimationConfigStack.concat([nextSceneConfig]);
const replacedStack = activeStack.slice(0, activeLength - 1).concat([route]);
this._emitWillFocus(nextStack[destIndex]);
this.setState({
routeStack: nextStack,
sceneConfigStack: nextAnimationConfigStack,
}, () => {
this._enableScene(destIndex);
this._transitionTo(destIndex, nextSceneConfig.defaultTransitionVelocity, null, () => {
this.immediatelyResetRouteStack(replacedStack);
});
});
};
After defining that in prototype, you can use it anywhere you want.
Just like
navigator.replaceWithAnimation(route);
Performace is too slow above provide method, can anyone optimized it
@waleedarshad-vf I used it in online production and after compiling them by react native and set to production mode. It's quite fluent. Have you tried the production mode?
@zhaotai thanks for you reply. I tried it again last day and it is working now. i don't know what happened that time. it might be due to low memory of the device.
@waleedarshad-vf Yeah, especially you develop in the simulator. The application memory will increase along with your code complication increasing and as a result the navigating and animation which is implemented by js code instead of native module will become slow and unsmooth.
@zhaotai one thing to be mention here that it is not as smooth as the push method of Navigator but thanks for Awesome work
@zhaotai it needs attention to make it as smooth as push method. the tester here points out this jerk animation issue in a single attempt.
@waleedarshad-vf I will try, thanks for suggestion. Maybe you can improve it, too.
Hi there! This issue is being closed because it has been inactive for a while.
But don't worry, it will live on with ProductPains! Check out its new home: https://productpains.com/post/react-native/navigator-animated-replace
ProductPains helps the community prioritize the most important issues thanks to its voting feature.
It is easy to use - just login with GitHub. GitHub issues have voting too, nevertheless
Product Pains has been very useful in highlighting the top bugs and feature requests:
https://productpains.com/product/react-native?tab=top
Also, if this issue is a bug, please consider sending a pull request with a fix.
We're a small team and rely on the community for bug fixes of issues that don't affect fb apps.
Most helpful comment
I have tried all of these solutions above but all didn't work.
So I had to check the Navigator source code while coded on my own. Finally after solving some problems such as flashing and animating, not working at all, replacing without animation though I have written the code, I got the solution.
These are my code below. Most of them are copied from Navigator source code. What I did is just combine them in a right way. Um...of course, they still can be optimized. And they too much hard to understand. Trust me, I won't understand after tonight. But it works. It's enough.
BTW, my RN is 0.27.
After defining that in prototype, you can use it anywhere you want.
Just like
navigator.replaceWithAnimation(route);