J'ai besoin d'empêcher le comportement par défaut de StackNavigator qui crée plusieurs copies d'un écran et les garde en mémoire (et a un comportement de démontage aléatoire). Je ne veux qu'une seule instance de chacun de mes écrans.
J'ai surchargé le routeur selon le code ci-dessous. Ce qu'il fait, c'est rechercher un écran existant dans la pile lors de la navigation et, s'il existe, le déplacer vers l'index actuel en mélangeant tout le reste. C'est potentiellement plus compliqué que nécessaire, mais il me semble que vous ne devriez pas jouer avec l'index actuel.
const AppNavigator = StackNavigator(Routes, {});
//move this into routes.js if it works
const prevGetStateForAction = AppNavigator.router.getStateForAction;
AppNavigator.router = {
...AppNavigator.router,
getStateForAction(action, state) {
//check if route is already on stack and go to it instead of adding a new item on stack
//NOTE: This will break things like being able to have push notification messages on the same route but different messages and being able to go back through them
//if that becomes a problem just opt out for certain route names eg if(action.routeName == "Messages") return prevGetStateForAction(aciton,state)
if(state && action.type == 'Navigation/NAVIGATE' && state.routes !== undefined ) {
//console.log("getStateForAction state",state,"action,",action);
var i = -1;
//try find the route in the stack
for(var c =0; c < state.routes.length; c++) {
if(state.routes[c].routeName == action.routeName) {
i = c;
break;
}
}
//found it but we're already there so do nothing as we're trying to navigate to ourselves
if(i == state.index)
return null;
//didn't find it - add screen to stack - ie call default action
if(i == -1)
return prevGetStateForAction(action,state);
//found it - move it to just after index and increment index - i think
console.log("stacknavigator is trying to duplicate route - moving back to previous route");
var route = state.routes[i];
var routes = state.routes.splice(i,1);
//you've just moved the item that index was pointing at if it was greater than where our item was
var newIndex = 0;
if(state.index > i)
newIndex = state.index -1;
else
newIndex = state.index;
//index was at the end so we just take the array and add our item at the end - no slices needed
if(state.index == state.routes.length -1)
var routes = [ ...routes, route];
else
var routes = [
...state.routes.slice(0,newIndex+1),
route,
...state.routes.slice(state.index+1)
];
return {
...state,
routes: routes,
index: newIndex
}
}
return prevGetStateForAction(action,state);
}
Cela fonctionne pendant un certain temps (c'est-à-dire que vous pouvez naviguer sans créer de doublons) mais finit par échouer et je n'arrive pas à comprendre pourquoi - vous pouvez voir mon appel console.log lorsqu'un doublon est atteint mais il n'est jamais atteint - le code s'exécute mais Cela pourrait être une erreur dans le code mais je pense que c'est plus probablement un comportement de React Navigation qui me manque. Il est possible que je ne prenne pas en compte le comportement des boutons Navigation/back et back.
Quelqu'un peut-il corriger mon code ou fournir une autre méthode pour empêcher les écrans en double dans la pile ?
edit - comme je ne peux pas insérer le code dans les commentaires
J'y ai réfléchi à nouveau et il m'a semblé que je ne devais probablement pas essayer de conserver le même index puisque mon écran se trouve désormais à la fin de la pile.
Quoi qu'il en soit, cela semble avoir résolu le problème car je n'ai pas pu reproduire un écran dupliqué.
const AppNavigator = StackNavigator(Routes, {});
//move this into routes.js if it works
const prevGetStateForAction = AppNavigator.router.getStateForAction;
AppNavigator.router = {
...AppNavigator.router,
getStateForAction(action, state) {
//check if route is already on stack and go to it instead of adding a new item on stack
//NOTE: This will break things like being able to have push notification messages on the same route but different messages and being able to go back through them
//if that becomes a problem just opt out for certain route names eg if(action.routeName == "Messages") return prevGetStateForAction(aciton,state)
if(state && action.type == 'Navigation/NAVIGATE' && state.routes !== undefined ) {
//console.log("getStateForAction state",state,"action,",action);
var i = -1;
//try find the route in the stack
for(var c =0; c < state.routes.length; c++) {
if(state.routes[c].routeName == action.routeName) {
i = c;
break;
}
}
//found it but we're already there so do nothing as we're trying to navigate to ourselves
if(i == state.index) {
console.log("getstateforaction() - you're trying to navigate to yourself!");
return null;
}
//didn't find it - add screen to stack - ie call default action
if(i == -1) {
console.log("getstateforaction() - no duplicate screen found");
return prevGetStateForAction(action,state);
}
//found it - move it to just after index and increment index - i think
console.log("stacknavigator is trying to duplicate route - moving back to previous route");
var route = state.routes[i];
var routes = state.routes;
routes.splice(i,1);
routes = [
...routes,
route
];
newIndex = routes.length-1;
return {
...state,
routes: routes,
index: newIndex
}
}
return prevGetStateForAction(action,state);
}