防止React Navigation StackNavigator

时间:2018-01-13 02:01:34

标签: react-native react-navigation

我需要阻止StackNavigator的默认行为,它创建多个屏幕副本并将它们保存在内存中(并具有随机卸载行为)。我只想要每个屏幕的一个实例。

我按照下面的代码覆盖了路由器。它的作用是在导航时查找堆栈中的现有屏幕,如果存在,则将其移动到当前索引,将其他所有内容混洗。它可能比需要的更复杂,但在我看来,你不应该乱用当前的索引。

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

它可以工作很长一段时间(因为你可以愉快地导航而不会创建重复项)但最终会失败,我无法解决原因 - 你可以看到我的console.log调用,当一个骗局被击中但是从来没有命中 - 代码正在运行但是。这可能是代码中的一个错误,但我觉得它更可能是我错过的React Navigation行为。可能是我没有考虑的导航/后退和后退按钮行为。

有人可以更正我的代码或提供防止堆叠中重复屏幕的替代方法吗?

编辑 - 因为我无法在评论中填写代码

我有另一个想法,在我看来,我可能不应该试图保持索引相同,因为我的屏幕现在将在堆栈的末尾。

无论如何,它似乎已经解决了问题,因为我无法重现重复的屏幕。

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

1 个答案:

答案 0 :(得分:0)

编辑后的回复中提供的代码解决了这个问题,并且已经工作了一个星期而没有任何问题。

我希望它可以帮助其他人面对同样的问题。