saa*_*adq 16 javascript ecmascript-6 reactjs redux
我一直在努力制作一个反应和减速秒表.我一直在努力弄清楚如何在redux中设计这样的东西.
首先想到的是有一个START_TIMER设置初始offset值的动作.在那之后,我用来反复setInterval触发一个TICK动作,通过使用偏移计算已经过了多少时间,将其添加到当前时间,然后更新offset.
这种方法似乎有效,但我不确定如何清除间隔来阻止它.此外,似乎这种设计很差,可能有更好的方法.
这是一个完整的JSFiddle,它具有START_TIMER正常运行的功能.如果您只是想看看我的减速机现在的样子,这里是:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
我真的很感激任何帮助.
Mic*_*ley 42
我可能会建议以不同的方式进行此操作:仅存储计算商店中已用时间所需的状态,并让组件设置自己的间隔,但通常他们希望更新显示.
这可以将操作调度保持在最低限度 - 仅调度启动和停止(和重置)计时器的操作.请记住,每次调度操作时都会返回一个新的状态对象,然后每个connected组件重新渲染(即使它们使用优化来避免在包装组件中重新渲染太多).此外,许多操作调度可能使调试应用程序状态更改变得困难,因为您必须TICK与其他操作一起处理所有s.
这是一个例子:
// 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);
Run Code Online (Sandbox Code Playgroud)
请注意,动作创建者和缩减器仅处理原始值,并且不使用任何类型的间隔或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);
Run Code Online (Sandbox Code Playgroud)
您甚至可以使用不同的更新频率在同一数据上显示多个计时器:
class Application extends React.Component {
render() {
return (
<div>
<Timer updateInterval={33} />
<Timer updateInterval={1000} />
</div>
);
}
}
Run Code Online (Sandbox Code Playgroud)
你可以在这里看到一个有效的JSBin:https://jsbin.com/dupeji/12/edit?js,output
and*_*ard 10
如果您打算在更大的应用程序中使用它,那么我会使用requestAnimationFrame而不是setInterval性能问题.当您显示毫秒时,您会在移动设备上注意到这一点,而不是在桌面浏览器上.
更新了JSFiddle
https://jsfiddle.net/andykenward/9y1jjsuz
您希望使用clearInterval带有调用结果的函数setInterval(唯一标识符)并停止该间隔执行任何进一步的操作.
因此,而不是声明一个setInterval内部start(),而是将其传递给reducer,以便它可以将其ID存储在状态:
interval作为操作对象的成员传递给调度程序
start() {
const interval = setInterval(() => {
store.dispatch({
type: 'TICK',
time: Date.now()
});
});
store.dispatch({
type: 'START_TIMER',
offset: Date.now(),
interval
});
}
Run Code Online (Sandbox Code Playgroud)
interval在START_TIMER动作减速器中存储新状态
case 'START_TIMER':
return {
...state,
isOn: true,
offset: action.offset,
interval: action.interval
};
Run Code Online (Sandbox Code Playgroud)
______
按照渲染组件 interval
通过在interval作为组件的属性:
const render = () => {
ReactDOM.render(
<Timer
time={store.getState().time}
isOn={store.getState().isOn}
interval={store.getState().interval}
/>,
document.getElementById('app')
);
}
Run Code Online (Sandbox Code Playgroud)
然后,我们可以检查out组件中的状态,以根据是否存在属性来呈现它interval:
render() {
return (
<div>
<h1>Time: {this.format(this.props.time)}</h1>
<button onClick={this.props.interval ? this.stop : this.start}>
{ this.props.interval ? 'Stop' : 'Start' }
</button>
</div>
);
}
Run Code Online (Sandbox Code Playgroud)
______
停止计时器
要停止计时器,我们清除使用的间隔,clearInterval然后initialState再次应用:
case 'STOP_TIMER':
clearInterval(state.interval);
return {
...initialState
};
Run Code Online (Sandbox Code Playgroud)
______
更新了JSFiddle
https://jsfiddle.net/8z16xwd2/2/
| 归档时间: |
|
| 查看次数: |
11665 次 |
| 最近记录: |