Путешествие во времени, вы это заслужили!

внешний интерфейс GitHub React.js Redux

Сколько времени на поездку?

Как следует из названия, вы можете передавать предыдущее и будущее в любое время, позволяя приложению переключаться в состояние в любое время. Все мы знаем, что общее состояние приложения очень сложное, создается, поддерживается, модифицируется, а сколько действий, которые повлияют на состояние, — дело непростое.

redux-devtools

Это на вершине вещей. Давайте будем следовать примеру работы вместе, чтобы понять принципы Redux Time Travel.

требования к чтению

  • основы реакции
  • Основа Redux, понимание взаимосвязи между действием, редьюсером и состоянием. Поймите принцип CombineReducer.

Начинать

Адрес проекта: (github)[GitHub.com/crowflyingjs/день…]

Предварительный просмотр:

Поскольку мы собираемся реализовать путешествие во времени, первым шагом, который нам нужен, является объект для записи каждого состояния:stateHistory.js

export default {
  past: [],
  futrue: [],
  present: undefined,
  gotoState(i) {
    const index = i * 1;
    const allState = [...this.past, this.present, ...this.futrue];
    this.present = allState[index];
    this.past = allState.slice(0, index);
    this.futrue = allState.slice(index + 1, allState.length);
  }
};

Мы делим состояния на три временных периода: прошлое, настоящее (есть только одно состояние) и будущее. Функция gotoState используется для путешествий во времени.Её реализация заключается в объединении всех состояний allState и их переназначении.Настоящему предшествует прошлое, а за ним следует будущее.

Итак, как мы сохраняем состояние каждого изменения? Нам нужно найти запись, и эта запись должна быть там, где срабатывает каждое изменение состояния. И единственный способ вызвать изменение состояния — этоdispatch(action), Подумайте об этом, похоже, есть только одно такое место, и студенты, которые видели исходный код редукса, должны быть знакомы с ним, это чистая функция редукторов, сгенерированных combReducer. combReducer отвечает за объединение нескольких редюсеров и, наконец, возвращает редюсер, который может обрабатывать все действия. Давайте сделаем это примерно просто:

const combineReducer = obj => (state, action) => {
  const finalState = {};
  for (key in obj) {
    finanlState[key] = obj[key](state[key], action);
  }
  return finalState; // 全局state
};

Далее воспользуемся идеей функционального программирования для улучшения функции Reducers, пусть запишет State:reducers.js

import stateHistory from './stateHistory';// 引入我们之前声明的history对象

// 原本我们是这样返回reducers的
export default combineReducers({
    books: fetchReducer,
    displayMode: bookDisplayReducer,
    currentStatus: statusReducer,
    topic: topicReducer
})
// 改造后如下:
export default history(
  combineReducers({
    books: fetchReducer,
    displayMode: bookDisplayReducer,
    currentStatus: statusReducer,
    topic: topicReducer
  })
);
// 我们用history包裹combineReducer,history实现如下
const history = reducers => (state, aciton) => {
  switch (action.type) {
    case 'UNDO': // 后退
      stateHistory.undo();
      break;
    case 'REDO': // 前进
      stateHistory.redo();
      break;
    case 'GOTO': // 定点指向
      stateHistory.gotoState(action.stateIndex);
      break;
    default:
      const newState = reducer(state, action);
      stateHistory.push(newState);// 每次dipatch(action)都会像将状态保存到stateHistory
  }
  return stateHistory.present; // 返回当前状态
}

идеальноstateHistory.js

export default {
  ...

  hasRecord(type) {// 查询是否有过去或者将来的状态
    return this[type].length > 0;
  },
  hasPresent() { // 查询是否有现在的状态
    return this.present !== undefined;
  },
  setPresent(state) {
    this.present = state;
  },
  movePresentToPast() {
    this.past.push(this.present);
  },
  push(currentState) { // 将状态都保存,并更新当前状态
    if (this.hasPresent()) {
      this.past.push(this.present);
    }
    this.setPresent(currentState);
  },
  getIndex() { // 获取当前状态index
    return this.past.length;
  },
  undo() { // 后退
    if (this.hasRecord('past')) {
      this.gotoState(this.getIndex() - 1);
    }
  },
  redo() { // 前进
    if (this.hasRecord('futrue')) {
      this.gotoState(this.getIndex() + 1);
    }
  },
  ...
};

Настройте действия:actions.js

...
export const redo = () => ({
  type: 'REDO'
});

export const undo = () => ({
  type: 'UNDO'
});

export const gotoState = stateIndex => ({
  type: 'GOTO',
  stateIndex
});

Подготовка завершена, и теперь мы можем напрямую добавить код триггера в компонент реакции.components/History.js

const History = ({ past, futrue, present, redo, undo, gotoState }) => {
  const styles = {
    container: {
      marginLeft: '20px',
      cursor: 'pointer'
    },

    link: { textDecoration: 'none' },
    input: { cursor: 'pointer' }
  };
  const RightArrow = () => (
    // 前进
    <a href="#" style={styles.link} onClick={() => redo()}>
      &#8594;
    </a>
  );

  const LeftArrow = () => (
    // 后退
    <a href="#" style={styles.link} onClick={() => undo()}>
      &#8592;
    </a>
  );
  const max = () =>
    (past ? past.length : 0) +
    (present ? 1 : 0) +
    (futrue ? futrue.length : 0) -
    1;
  const value = () => (past ? past.length : 0);
  return (
    <span>
      <input
        type="range"
        min={0}
        max={max()}
        value={value()}
        onChange={e => {
          gotoState(e.target.value);
        }}
        style={styles.input}
      />
      {past && past.length > 0 ? <LeftArrow /> : null}
      {futrue && futrue.length > 0 ? <RightArrow /> : null}
    </span>
  );
};

над! Я надеюсь, что это поможет вам понять Redux.