3 votes

Empêcher les écrans en double dans React Navigation StackNavigator

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);
            }

1voto

kbcool Points 153

Le code fourni dans la réponse éditée a résolu le problème et fonctionne depuis une semaine maintenant sans aucun problème.

J'espère que cela aidera quelqu'un d'autre confronté au même problème.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X