我一直在试图做一个反应和减速的秒表.弄清楚如何在redux中设计这样的东西,我一直很麻烦.
想到的第一件事就是采取了一项START_TIMER动作来设定初始补偿值.之后,我使用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; } }
我真的很感激任何帮助.
解决方法
我可能建议使用不同的方式:仅存储计算商店中经过的时间所需的状态,并且让组件设置自己的间隔,但他们经常希望更新显示.
这样可以将动作调度保持在最小限度 – 只会启动和停止(并重置)定时器的动作.请记住,每次调度一个动作时,都会返回一个新的状态对象,然后每个连接的组件都会重新呈现(即使它们使用优化来避免包装组件中的重新渲染过多).此外,许多动作调度可能使得难以调试应用程序状态更改,因为您必须处理所有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);
请注意,操作创建者和缩减器仅处理原始值,并且不使用任何间隔或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,stoppedAt } = this.props; const elapsed = getElapsedTime(baseTime,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,stoppedAt } = state; return { baseTime,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