我一直在试图做一个反应和减速的秒表.弄清楚如何在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