使用 redux 创建秒表 [英] Creating a stopwatch with redux

查看:24
本文介绍了使用 redux 创建秒表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

这种方法似乎有效,但我不确定如何清除间隔以阻止它.另外,这个设计似乎很糟糕,可能有更好的方法来做到这一点.

这是一个完整的 JSFiddle,它具有 START_TIMER 功能.如果你现在只想看看我的减速器是什么样子,这里是:

const initialState = {是:假,时间:0};const timer = (state = initialState, action) =>{开关(动作.类型){案例START_TIMER":返回 {...状态,isOn: 真,偏移量:action.offset};案例STOP_TIMER":返回 {...状态,isOn: 假};案例'TICK':返回 {...状态,时间:state.time + (action.time - state.offset),偏移量:action.time};默认:返回状态;}}

我真的很感激任何帮助.

解决方案

我可能会建议采用不同的方式:仅在存储中存储计算经过时间所需的状态,并让组件自行设置/em> 间隔他们希望更新显示的频率.

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

这是一个例子:

//动作创建者函数 startTimer(baseTime = 0) {返回 {类型:START_TIMER",基准时间:基准时间,现在:新日期().getTime()};}函数 stopTimer() {返回 {类型:STOP_TIMER",现在:新日期().getTime()};}函数resetTimer() {返回 {类型:RESET_TIMER",现在:新日期().getTime()}}//减速器/存储常量初始状态 = {开始于:未定义,停止于:未定义,基准时间:未定义};函数减速器(状态 = 初始状态,动作){开关(动作.类型){案例RESET_TIMER":返回 {...状态,基本时间:0,开始: state.startedAt ?action.now:未定义,停止在: state.stoppedAt ?action.now : 未定义};案例START_TIMER":返回 {...状态,baseTime: action.baseTime,开始于:action.now,停止于:未定义};案例STOP_TIMER":返回 {...状态,停止于:action.now}默认:返回状态;}}const store = createStore(reducer);

注意动作创建者和化简器只处理原始值,不使用任何类型的间隔或 TICK 动作类型.现在,组件可以轻松订阅此数据并根据需要随时更新:

//获取存储状态的辅助函数//并返回当前经过的时间函数getElapsedTime(baseTime,startedAt,stoppedAt = new Date().getTime()){如果 (!startedAt) {返回0;} 别的 {返回stoppedAt-startedAt+baseTime;}}class Timer 扩展了 React.Component {componentDidMount() {this.interval = setInterval(this.forceUpdate.bind(this), this.props.updateInterval || 33);}componentWillUnmount() {clearInterval(this.interval);}使成为() {const { baseTime,startedAt,stoppedAt } = this.props;const elapsed = getElapsedTime(baseTime,startedAt,stoppedAt);返回 (<div><div>时间:{elapsed}</div><div><button onClick={() =>this.props.startTimer(elapsed)}>Start</button><button onClick={() =>this.props.stopTimer()}>停止</button><button onClick={() =>this.props.resetTimer()}>重置</button>

);}}函数 mapStateToProps(state) {const { baseTime,startedAt,stoppedAt } = 状态;返回 { baseTime,startedAt,stoppedAt};}Timer = ReactRedux.connect(mapStateToProps, { startTimer, stopTimer, resetTimer })(Timer);

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

class Application 扩展 React.Component {使成为() {返回 (<div><定时器更新间隔={33}/><定时器更新间隔={1000}/>

);}}

您可以在此处查看使用此实现的 正在运行的 JSBin:https://jsbin.com/dupeji/12/edit?js,output

I've been trying to make a stopwatch in react and redux. I've been having trouble trouble figuring out how to design such a thing in redux.

The first thing that came to mind was having a START_TIMER action which would set the initial offset value. Right after that, I use setInterval to fire off a TICK action over and over again that calculates how much time has passed by using the offset, adds it to the current time, and then updates the offset.

This approach seems to work, but I'm not sure how I would clear the interval to stop it. Also, it seems like this design is poor and there is probably a better way to do it.

Here is a full JSFiddle that has the START_TIMER functionality working. If you just want to see what my reducer looks like right now, here it is:

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

I would really appreciate any help.

解决方案

I would probably recommend going about this differently: store only the state necessary to calculate the elapsed time in the store, and let components set their own interval for however often they wish to update the display.

This keeps action dispatches to a minimum — only actions to start and stop (and reset) the timer are dispatched. Remember, you're returning a new state object every time you dispatch an action, and each connected component then re-renders (even though they use optimizations to avoid too many re-renders inside the wrapped components). Furthermore, many many action dispatches can make it difficult to debug app state changes, since you have to deal with all the TICKs alongside the other actions.

Here's an example:

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

Notice the action creators and reducer deals only with primitive values, and does not use any sort of interval or a TICK action type. Now a component can easily subscribe to this data and update as often as it wants:

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

You could even display multiple timers on the same data with a different update frequency:

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

You can see a working JSBin with this implementation here: https://jsbin.com/dupeji/12/edit?js,output

这篇关于使用 redux 创建秒表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆