React Navigation源代码阅读 :StateUtils.js

import invariant from './utils/invariant';

/**
 * Utilities to perform atomic operation with navigate state and routes.
 *
 * 对导航状态和路由执行原子操作的工具方法
 *
 * 导航状态可以理解成这样一个对象 :
 * 1. 拥有一个属性 路由栈 routes, 是一个即是数组又是栈的数据结构,用于跟踪记录用户对屏幕的访问,栈中的每个元素,也就是栈的一帧,
 *    表示一个路由(可以是一个普通屏幕组件,也可以是一个嵌套的路由器);对路由栈的操作可以是压栈出栈,也可以不是压栈出栈而只是更新
 *    index 指向另外一个存在于路由栈中的路由;
 * 2. 拥有一个属性 当前路由索引 index, 表示当前活跃路由,指向路由栈中的某一个路由,也就是当前路由器作为活跃屏幕渲染的子路由;
 * 3. 当前路由索引 index 并不总是指向路由栈的栈顶:
 * 3.1 如果仅仅使用 PUSH/POP 操作,当前路由索引 index 总是指向路由栈栈顶;
 * 3.2 如果不仅仅使用 PUSH/POP 操作,还是用 jumpXXX/forward 之类的操作, 当前路由索引 index 就不一定指向路由栈栈顶;
 *
 * 一个路由器定义的时候被提供了各种配置信息:路由配置,导航配置,而在路由器实例化的时候,其导航状态对象会被创建,该导航状态的
 * 初始值一般是这样的 :
 * 1. index 为 0;
 * 2. routes 为1个元素的数组,这个元素指向该路由器的初始路由(可以是普通屏幕组件,也可以是嵌套的路由器);
 *
 * ```javascript
 * const state1 = {key: 'screen 1'};
 * const state2 = NavigationStateUtils.push(state1, {key: 'screen 2'});
 * ```
 */
const StateUtils = {

    /**
     * 获取状态对象的路由栈中 key 属性等于参数 key 的路由
     * Gets a route by key. If the route isn't found, returns `null`.
     * @param state
     * @param key
     * @return {*|null} 如果没有找到,返回null
     */
    get(state, key) {
        return state.routes.find(route => route.key === key) || null;
    },

    /**
     *
     * 获取状态对象的路由栈中 key 属性等于参数 key 的路由的索引(栈底路由的索引为0)
     * Returns the first index at which a given route's key can be found in the
     * routes of the navigation state, or -1 if it is not present.
     *
     * @param state
     * @param key
     * @return {*} 如果没找到,返回 -1
     */
    indexOf(state, key) {
        return state.routes.map(route => route.key).indexOf(key);
    },

    /**
     *
     * 检查是否有 key 属性等于参数 key 的路由存在于当前路由栈中
     *
     * Returns `true` at which a given route's key can be found in the
     * routes of the navigation state.
     *
     * @param state
     * @param key
     * @return {boolean} `true` 表示存在,`false` 表示不存在
     */
    has(state, key) {
        return !!state.routes.some(route => route.key === key);
    },

    /**
     * 新的路由压入到路由栈的栈顶,然后 导航状态对象的属性 index 被更新指向路由栈顶路由
     *
     * 注意 : 听起来PUSH是一个修改操作,但实际上,该方法是基于原导航状态对象和新的待压入路由,
     * 构造了一个新的导航状态对象(index + 新的路由栈)
     *
     * @return 动作完成后新建的导航状态对象
     *
     * Pushes a new route into the navigation state.
     * Note that this moves the index to the positon to where the last route in the
     * stack is at.
     */
    push(state, route) {
        invariant(
            StateUtils.indexOf(state, route.key) === -1,
            'should not push route with duplicated key %s',
            route.key
        );

        const routes = state.routes.slice();
        routes.push(route);

        return {
            ...state,
            index: routes.length - 1,
            routes,
        };
    },

    /**
     *
     * 从当前导航状态对象的路由栈顶部弹出一个路由,然后 导航状态对象的属性 index 被更新指向路由栈顶路由
     *
     * 注意 : 听起来POP是一个修改操作,但实际上,该方法是基于原导航状态对象,构造了一个新的导航状态对象(index + 新的路由栈)
     *
     * @return 动作完成后新建的导航状态对象
     * Pops out a route from the navigation state.
     * Note that this moves the index to the positon to where the last route in the
     * stack is at.
     */
    pop(state) {
        if (state.index <= 0) {
            // [Note]: Over-popping does not throw error. Instead, it will be no-op.
            return state;
        }
        const routes = state.routes.slice(0, -1);
        return {
            ...state,
            index: routes.length - 1,
            routes,
        };
    },

    /**
     *
     * 没有路由出栈也没有压栈操作,只是当前路由索引index更新到参数index指定的某个路由,
     *
     *
     * @return 动作完成后新建的导航状态对象(如果目标路由和当前路由是同一个路由,则直接返回原路由状态对象)
     *
     * Sets the focused route of the navigation state by index.
     */
    jumpToIndex(state, index) {
        if (index === state.index) {
            return state;
        }

        invariant(!!state.routes[index], 'invalid index %s to jump to', index);

        return {
            ...state,
            index,
        };
    },

    /**
     *
     * 没有路由出栈也没有压栈操作,只是当前路由索引index更新到参数key指定的某个路由,
     *
     * @return 动作完成后新建的导航状态对象(如果目标路由和当前路由是同一个路由,则直接返回原路由状态对象)
     *
     * Sets the focused route of the navigation state by key.
     */
    jumpTo(state, key) {
        const index = StateUtils.indexOf(state, key);
        return StateUtils.jumpToIndex(state, index);
    },

    /**
     * 返回操作,修改当前路由index指向导航栈中当前路由下面的那个路由,
     *
     * @return 动作完成后新建的导航状态对象(如果当前路由路由栈中只有一个路由,那么什么都不做,返回原导航状态对象)
     *
     * Sets the focused route to the previous route.
     */
    back(state) {
        const index = state.index - 1;
        const route = state.routes[index];
        return route ? StateUtils.jumpToIndex(state, index) : state;
    },

    /**
     * 前进操作,修改当前路由index指向路由栈中当前路由上面的那个路由,
     *
     * @return 动作完成后新建的导航状态对象(如果当前路由已经是栈顶路由,那么什么都不做,返回原导航状态对象)
     *
     * Sets the focused route to the next route.
     */
    forward(state) {
        const index = state.index + 1;
        const route = state.routes[index];
        return route ? StateUtils.jumpToIndex(state, index) : state;
    },

    /**
     * 将导航状态 state 的路由栈中 key 指定的路由替换成 route 指定的路由信息,
     * 并将导航状态 state 的当前路由索引 index 指向该路由
     *
     * @return 动作完成后新建的导航状态对象(如果待替换路由和参数路由其实是一个对象,那么什么都不做,返回原导航状态对象)
     *
     * Replace a route by a key.
     * Note that this moves the index to the positon to where the new route in the
     * stack is at.
     */
    replaceAt(state, key, route) {
        const index = StateUtils.indexOf(state, key);
        return StateUtils.replaceAtIndex(state, index, route);
    },

    /**
     *
     * 将导航状态 state 的路由栈中 index 指定的路由替换成 route 指定的路由信息,
     * 并将导航状态 state 的当前路由索引 index 指向该路由
     *
     * @return 动作完成后新建的导航状态对象(如果待替换路由和参数路由其实是一个对象,那么什么都不做,返回原导航状态对象)
     *
     * Replace a route by a index.
     * Note that this moves the index to the positon to where the new route in the
     * stack is at.
     */
    replaceAtIndex(state, index, route) {
        invariant(
            !!state.routes[index],
            'invalid index %s for replacing route %s',
            index,
            route.key
        );

        if (state.routes[index] === route) {
            return state;
        }

        const routes = state.routes.slice();
        routes[index] = route;

        return {
            ...state,
            index,
            routes,
        };
    },

    /**
     * 将导航状态对象 state 复位为参数 routes 和 index 指定的信息,
     * 其实可以理解为使用 routes 和 index 重新构建一个新的导航状态对象
     *
     * @return 动作完成后新建的导航状态对象(如果原状态对象和目标状态对象相等,那么什么都不做,返回原导航状态对象)
     * Resets all routes.
     * Note that this moves the index to the positon to where the last route in the
     * stack is at if the param `index` isn't provided.
     */
    reset(state, routes, index) {
        invariant(
            routes.length && Array.isArray(routes),
            'invalid routes to replace'
        );

        const nextIndex = index === undefined ? routes.length - 1 : index;

        // 如果原来状态对象和将要设置的属性(index,路由栈)相等,则之间返回原状态对象
        if (state.routes.length === routes.length && state.index === nextIndex) {
            // 原状态对象的index和目标状态对象的index相等
            const compare = (route, ii) => routes[ii] === route;
            if (state.routes.every(compare)) {
                // 原状态对象的路由栈和目标状态对象的路由栈相等:
                // 1. 栈中路由数量相等;
                // 2. 每一帧按顺序比较也相等;
                return state;
            }
        }

        invariant(!!routes[nextIndex], 'invalid index %s to reset', nextIndex);

        return {
            ...state,
            index: nextIndex,
            routes,
        };
    },
};

export default StateUtils;

猜你喜欢

转载自blog.csdn.net/andy_zhang2007/article/details/80313413