javascript - 使用 redux 创建秒表

标签 javascript reactjs ecmascript-6 redux

我一直在尝试在 React 和 Redux 中制作秒表。我一直无法弄清楚如何在 redux 中设计这样的东西。

首先想到的是 START_TIMER 操作,它会设置初始 offset 值。在那之后,我使用 setInterval 一遍又一遍地触发 TICK 操作,通过使用偏移量计算已经过去了多少时间,将其添加到当前时间,然后更新 offset

这种方法似乎可行,但我不确定如何清除间隔以停止它。此外,这种设计似乎很糟糕,可能有更好的方法。

这是完整的 JSFiddle具有 START_TIMER 功能的工作。如果你只是想看看我的 reducer 现在是什么样子,这里是:

const initialState = {
  isOn: false,
  time: 0
};

const timer = (state = initialState, action) => {
  switch (action.type) {
    case 'START_TIMER':
      return {
        ...state,
        isOn: true,
        offset: action.offset
      };

    case 'STOP_TIMER':
      return {
        ...state,
        isOn: false
      };

    case 'TICK':
      return {
        ...state,
        time: state.time + (action.time - state.offset),
        offset: action.time
      };

    default: 
      return state;
  }
}

如果有任何帮助,我将不胜感激。

最佳答案

我可能会建议采用不同的方法:仅存储计算耗时所需的状态,并让组件设置它们自己的时间间隔,无论它们希望更新显示的频率如何。

这可以将操作分派(dispatch)保持在最低限度——只分派(dispatch)启动和停止(以及重置)计时器的操作。请记住,每次您分派(dispatch)一个 Action 时,您都会返回一个新的状态对象,然后每个 connected 组件都会重新呈现(即使它们使用优化来避免太多在包装的组件内重新渲染)。此外,许多操作分派(dispatch)会使调试应用状态更改变得困难,因为您必须与其他操作一起处理所有 TICK

这是一个例子:

// Action Creators

function startTimer(baseTime = 0) {
  return {
    type: "START_TIMER",
    baseTime: baseTime,
    now: new Date().getTime()
  };
}

function stopTimer() {
  return {
    type: "STOP_TIMER",
    now: new Date().getTime()
  };
}

function resetTimer() {
  return {
    type: "RESET_TIMER",
    now: new Date().getTime()
  }
}


// Reducer / Store

const initialState = {
  startedAt: undefined,
  stoppedAt: undefined,
  baseTime: undefined
};

function reducer(state = initialState, action) {
  switch (action.type) {
    case "RESET_TIMER":
      return {
        ...state,
        baseTime: 0,
        startedAt: state.startedAt ? action.now : undefined,
        stoppedAt: state.stoppedAt ? action.now : undefined
      };
    case "START_TIMER":
      return {
        ...state,
        baseTime: action.baseTime,
        startedAt: action.now,
        stoppedAt: undefined
      };
    case "STOP_TIMER":
      return {
        ...state,
        stoppedAt: action.now
      }
    default:
      return state;
  }
}

const store = createStore(reducer);

注意 action creators 和 reducer 只处理原始值,不使用任何类型的间隔或 TICK 操作类型。现在,组件可以轻松订阅此数据并根据需要随时更新:

// Helper function that takes store state
// and returns the current elapsed time
function getElapsedTime(baseTime, startedAt, stoppedAt = new Date().getTime()) {
  if (!startedAt) {
    return 0;
  } else {
    return stoppedAt - startedAt + baseTime;
  }
}

class Timer extends React.Component {
  componentDidMount() {
    this.interval = setInterval(this.forceUpdate.bind(this), this.props.updateInterval || 33);
  }

  componentWillUnmount() {
    clearInterval(this.interval);
  }

  render() {
    const { baseTime, startedAt, stoppedAt } = this.props;
    const elapsed = getElapsedTime(baseTime, startedAt, stoppedAt);

    return (
      <div>
        <div>Time: {elapsed}</div>
        <div>
          <button onClick={() => this.props.startTimer(elapsed)}>Start</button>
          <button onClick={() => this.props.stopTimer()}>Stop</button>
          <button onClick={() => this.props.resetTimer()}>Reset</button>
        </div>
      </div>
    );
  }
}

function mapStateToProps(state) {
  const { baseTime, startedAt, stoppedAt } = state;
  return { baseTime, startedAt, stoppedAt };
}

Timer = ReactRedux.connect(mapStateToProps, { startTimer, stopTimer, resetTimer })(Timer);

您甚至可以在同一数据上以不同的更新频率显示多个计时器:

class Application extends React.Component {
  render() {
    return (
      <div>
        <Timer updateInterval={33} />
        <Timer updateInterval={1000} />
      </div>
    );
  }
}

你可以看到一个working JSBin在此处执行此操作:https://jsbin.com/dupeji/12/edit?js,output

关于javascript - 使用 redux 创建秒表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34577012/

相关文章:

javascript - 我用babel编译js的时候没有定义Proxy

reactjs - 使用继承的 ES6 React 类时未触发 componentDidMount 方法

javascript - 使用 npm 安装或更新所需的包,就像 ruby​​gems 的 bundler

javascript - 从 JSON 数组获取列表结果

javascript - React Component - 多次重新加载数据

reactjs - 如何使用 jest 和 RTL 模拟来自 react 组件的异步操作调用

javascript - 为什么我的三元运算符会出现这些错误?

javascript - 在 Node module.exports = {} 中导出一个类

javascript - 检查 if/en/or/es/in document.location

ASP.NET 菜单父菜单项在启用浮出控件时在悬停时突出显示