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

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

问题描述

我一直在试图做一个秒表的反应和减少。我在解决如何在redux中设计这样的事情时遇到麻烦。



首先想到的是一个 START_TIMER 动作,设置初始 offset value。之后,我使用 setInterval 一次又一次地发出一个 TICK 动作,计算出多少时间通过使用偏移量,将其添加到当前时间,然后更新 offset



这种方法似乎工作,但我不知道如何清除间隔停止它。此外,似乎这样的设计很差,可能还有更好的方法。



这是一个完整的 START_TIMER 功能的网络/ a94jabzd / 1 /rel =noreferrer> JSFiddle 。如果你现在只想看看我的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
};

默认值:
返回状态;
}
}

我真的非常感谢任何帮助。

解决方案

我可能建议使用不同的方式:仅存储计算商店中经过的时间所需的状态,并让组件设置他们的自己的间隔,通常他们希望更新显示。



这样可以将动作调度保持在最低限度 - 只有启动和停止(并重置) )计时器被调度。记住,你每次都会返回一个新的状态对象,你分派一个动作,然后每个连接 ed组件然后重新渲染(即使他们使用优化来避免包装组件中的重新渲染太多)。此外,许多行动调度可能会使应用程序状态更改变得困难,因为您必须处理所有 TICK 以及其他操作。



这里有一个例子:

  // Action Creators 

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

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

函数reducer(state = initialState,action){
switch(action.type){
caseRESET_TIMER:
return {
.. .state,
baseTime:0,
startedAt:state.startedAt? action.now:undefined,
stoppedAt:state.stoppedAt? action.now:undefined
};
caseSTART_TIMER:
return {
... state,
baseTime:action.baseTime,
startedAt:action.now,
stoppedAt:未定义
};
caseSTOP_TIMER:
return {
... state,
stoppedAt:action.now
}
默认值:
返回状态;
}
}

const store = createStore(reducer);

请注意,操作创建者和缩减器仅处理原始值,不使用任何间隔或一个 TICK 动作类型。现在,组件可以轻松地订阅这些数据并按照需要进行更新:

  //需要存储状态的助手功能
//并返回当前已用时间
函数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.props.updateInterval || 33);
}

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

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

return(
< div>
< div>时间:{已过}< / div>
< div>
< button onClick = {()=> this.props.startTimer(elapsed)}>开始< / button>
< button onClick = {()=> this.props.stopTimer()}>停止< / button>
< button onClick = {()=> this.props.resetTimer()}>重置< / button>
< / div>
< / div>
);
}
}

函数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>
);
}
}

你可以看到一个使用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天全站免登陆