zoukankan      html  css  js  c++  java
  • React Native之RNRF框架源码解析

     说是源码解析,但是目前对JS语法还不算熟,只能算是分析下,因为官方的文档实在是少的可伶

    import Reducer, { findElement } from './Reducer';
    .
    .
    .
           const initialState = getInitialState(scenesMap);
        const reducerCreator = props.createReducer || Reducer;
    
        const routerReducer = props.reducer || (
          reducerCreator({
            initialState,
            scenes: scenesMap,
          }));
    
        return routerReducer;

    在RNRF中,有一个Reducer,如果createReducer属性未定义,则使用默认的Reducer(这是一个方法) 

    而我们目前写的createReducer是这样的:

    const reducerCreate = params => {
        const defaultReducer = new Reducer(params);
        return (state, action) => {
            console.log('ACTION:', action);
            return defaultReducer(state, action);
        };
    };

    所以最终还是会触发RNRF中的Reducer方法

    return (stateParam, actionParam) => {
        let state = stateParam;
        let action = actionParam;
        state = state || { ...initialState, scenes };
        assert(action, 'action should be defined');   //assert方法只是简单的判断第一个参数是否存在if(param1),不存在则抛出Error
        assert(action.type, 'action type should be defined');
        assert(state.scenes, 'state.scenes is missed');
    
        if (action.key) {
          if (ActionMap[action.type] === ActionConst.REFRESH) {
            let key = action.key;
            let child = findElement(state, key, action.type) || state.scenes[key];
            let sceneKey = child.sceneKey;
            if (child.base) {
              child = { ...state.scenes[child.base], ...child };
              assert(state.scenes[child.base], `No scene exists for base=${child.base}`);
              key = state.scenes[child.base].key;
              sceneKey = state.scenes[child.base].sceneKey;
            }
            assert(child, `missed child data for key=${key}`);
            // evaluate functions within actions to allow conditional set, like switch values
            const evaluated = {};
            Object.keys(action).forEach(el => {
              if (typeof action[el] === 'function' && typeof child[el] !== 'undefined'
                && typeof child[el] !== typeof action[el]) {
                evaluated[el] = action[el](child[el], child);
              }
            });
            action = { ...child, ...action, ...evaluated, sceneKey, key };
    
            // console.log("REFRESH ACTION:", action);
          } else {
            const scene = state.scenes[action.key];  //state.scenes 中包含所有的scene(包括子scene)
            assert(scene, `missed route data for key=${action.key}`);
            // clone scene  克隆当前的scene
            if (scene.clone) {
              action.parent = getCurrent(state).parent;
            }
          }
        } else {
          // set current route for pop action or refresh action
          if (ActionMap[action.type] === ActionConst.BACK_ACTION ||
              ActionMap[action.type] === ActionConst.BACK ||
              ActionMap[action.type] === ActionConst.POP_AND_REPLACE ||
              ActionMap[action.type] === ActionConst.REFRESH ||
              ActionMap[action.type] === ActionConst.POP_TO) {
            if (!action.key && !action.parent) {
              action = { ...getCurrent(state), ...action };
            }
          }
    
          // Find the parent and index of the future state
          if (ActionMap[action.type] === ActionConst.POP_TO) {
            /*
             * if a string is passed as only argument
             * Actions.filterParam will put it in the data property
             * otherwise look for the scene property
             */
            const target = action.data || action.scene;
            assert(target, 'PopTo() must be called with a single argument: ' +
            'either the scene name (string) or an object with within the scene property ' +
            'carrying the target scene to pop to');
    
            const targetEl = findElement(state, target, action.type);
            assert(targetEl, `Cannot find element name named ${target} within current state`);
    
            // target is a node
            let parent = targetEl.sceneKey;
            let targetIndex = 0;
    
            // target is child of a node
            if (!targetEl.children) {
              const targetParent = findElement(state, targetEl.parent, action.type);
              assert(targetParent, `Cannot find parent for target ${target}`);
              parent = targetParent.sceneKey;
    
              targetIndex = targetParent.children.indexOf(targetEl);
              assert(targetIndex > -1, `${target} does not belong to ${targetParent.sceneKey}`);
            }
    
            action.parent = parent;
            action.targetIndex = targetIndex;
          }
    
          // recursive pop parent
          if (ActionMap[action.type] === ActionConst.BACK_ACTION ||
              ActionMap[action.type] === ActionConst.BACK ||
              ActionMap[action.type] === ActionConst.POP_AND_REPLACE) {
            const parent = action.parent || state.scenes[action.key].parent;
            let el = findElement(state, parent, action.type);
            while (el.parent && (el.children.length <= 1 || el.tabs)) {
              el = findElement(state, el.parent, action.type);
              assert(el, `Cannot find element for parent=${el.parent} within current state`);
            }
            action.parent = el.sceneKey;
          }
        }
    
        switch (ActionMap[action.type]) {
          case ActionConst.BACK:
          case ActionConst.BACK_ACTION:
          case ActionConst.POP_AND_REPLACE:
          case ActionConst.POP_TO:
          case ActionConst.REFRESH:
          case ActionConst.PUSH:
          case ActionConst.PUSH_OR_POP:
          case ActionConst.JUMP:
          case ActionConst.REPLACE:
          case ActionConst.RESET:
            return update(state, action);
    
          default:
            return state;
    
        }
      };
    export function getCurrent(state) {
      if (!state.children) {
        return state;
      }
      return getCurrent(state.children[state.index]);
    }
    
    function update(state, action) {
      // find parent in the state
      const props = { ...state.scenes[action.key], ...action };
      assert(props.parent, `No parent is defined for route=${action.key}`);
      return inject(state, action, props, state.scenes);
    }
    //这个就是reducer的核心方法了
    function inject(state, action, props, scenes) {
    const condition = ActionMap[action.type] === ActionConst.REFRESH ? state.key === props.key || state.sceneKey === action.key : state.sceneKey === props.parent; // console.log("INJECT:", action.key, state.sceneKey, condition); if (!condition) { if (state.children) { const res = state.children.map(el => inject(el, action, props, scenes)); let changed = false; let changedIndex = -1; for (let i = 0; i < res.length; i++) { if (res[i] !== state.children[i]) { changed = true; changedIndex = i; break; } } return changed ? { ...state, children: res, index: changedIndex } : state; } return state; } let ind; switch (ActionMap[action.type]) { case ActionConst.POP_TO: { const targetIndex = action.targetIndex; return { ...state, index: targetIndex, children: refreshTopChild(state.children.slice(0, (targetIndex + 1)), action.refresh), }; } case ActionConst.BACK: case ActionConst.BACK_ACTION: { assert(!state.tabs, 'pop() operation cannot be run on tab bar (tabs=true)'); if (Platform.OS === 'android') { assert(state.index > 0, 'You are already in the root scene.'); } if (state.index === 0) { return state; } let popNum = 1; if (action.popNum) { assert(typeof(action.popNum) === 'number', 'The data is the number of scenes you want to pop, it must be Number'); popNum = action.popNum; assert(popNum % 1 === 0, 'The data is the number of scenes you want to pop, it must be integer.'); assert(popNum > 1, 'The data is the number of scenes you want to pop, it must be bigger than 1.'); assert(popNum <= state.index, 'The data is the number of scenes you want to pop, ' + "it must be smaller than scenes stack's length."); } return { ...state, index: state.index - popNum, from: state.children[state.children.length - popNum], children: refreshTopChild(state.children.slice(0, -1 * popNum), action.refresh), }; } // This action will pop the scene stack and then replace current scene in one go case ActionConst.POP_AND_REPLACE: { assert(!state.tabs, 'pop() operation cannot be run on tab bar (tabs=true)'); assert(state.index > 0, 'You are already in the root scene.'); let popNum = 1; if (action.popNum) { assert(typeof(action.popNum) === 'number', 'The data is the number of scenes you want to pop, it must be Number'); popNum = action.popNum; assert(popNum % 1 === 0, 'The data is the number of scenes you want to pop, it must be integer.'); assert(popNum > 1, 'The data is the number of scenes you want to pop, it must be bigger than 1.'); assert(popNum <= state.index, 'The data is the number of scenes you want to pop, ' + "it must be smaller than scenes stack's length."); } state = { ...state, index: state.index - popNum, from: state.children[state.children.length - popNum], children: state.children.slice(0, -1 * popNum), }; if (state.children[state.index].sceneKey === action.key) { return state; } const newAction = { duration: 0, // do not animate ...action, }; delete newAction.popNum; const newProps = { ...props }; delete newProps.popNum; state.children[state.children.length - 1] = getInitialState( newProps, scenes, state.index, newAction ); return { ...state, children: state.children }; } case ActionConst.REFRESH: return props.base ? { navBar: state.navBar, ...scenes.rootProps, ...props, key: state.key, from: null } : { ...state, ...props, key: state.key, from: null, }; case ActionConst.PUSH_OR_POP: ind = state.children.findIndex(el => el.sceneKey === action.key); if (ind !== -1) { return { ...state, index: ind, from: state.children[state.index], children: refreshTopChild(state.children.slice(0, ind + 1), action.refresh), }; } return { ...state, index: state.index + 1, from: null, children: [...state.children, getInitialState(props, scenes, state.index + 1, action)], }; case ActionConst.PUSH: if (state.children[state.index].sceneKey === action.key && !props.clone && checkPropertiesEqual(action, state.children[state.index])) { return state; } return { ...state, index: state.index + 1, from: null, children: [...state.children, getInitialState(props, scenes, state.index + 1, action)], }; case ActionConst.JUMP: { assert(state.tabs, `Parent=${state.key} is not tab bar, jump action is not valid`); ind = -1; state.children.forEach((c, i) => { if (c.sceneKey === action.key) { ind = i; } }); assert(ind !== -1, `Cannot find route with key=${action.key} for parent=${state.key}`); if (action.unmountScenes) { resetHistoryStack(state.children[ind]); } return { ...state, index: ind }; } case ActionConst.REPLACE: if (state.children[state.index].sceneKey === action.key) { return state; } state.children[state.children.length - 1] = getInitialState( props, scenes, state.index, action ); return { ...state, children: state.children }; case ActionConst.RESET: if (state.children[state.index].sceneKey === action.key) { return state; } state.children = state.children.splice(0, 1); state.children[0] = getInitialState(props, scenes, state.index, action); return { ...state, index: 0, from: null, children: state.children, }; default: return state; } }
  • 相关阅读:
    MySQL递归查询树状表的子节点、父节点
    ajax传递参数给springmvc总结[转]
    转:Java中String与byte[]的转换
    easymock的用法
    java的反射
    spring的依赖注入
    等页面加载完
    ajax请求
    对象的复制
    springJDBC
  • 原文地址:https://www.cnblogs.com/yz1311/p/6185799.html
Copyright © 2011-2022 走看看