我正在尝试使用React和Redux构建一个计时器。
我尽可能地分开关注。请帮我找一个有效的解决方案!谢谢。
错误:未捕获的TypeError:无法读取未定义(...)
的属性“subscribe”计时器组件
Timer.js
import React, { Component } from 'react';
import timer from '../reducers/index.js';
import store from '../stores/timerStore.js';
// React Component to display the timer
class Timer extends Component {
constructor() {
super();
this.start = this.start.bind(this);
this.stop = this.stop.bind(this);
}
start() {
store.dispatch({
type: 'START_TIMER',
offset: Date.now(),
});
}
stop() {
store.dispatch({
type: 'STOP_TIMER'
});
}
format(time) {
const pad = (time, length) => {
while (time.length < length) {
time = '0' + time;
}
return time;
}
time = new Date(time);
let m = pad(time.getMinutes().toString(), 2);
let s = pad(time.getSeconds().toString(), 2);
let ms = pad(time.getMilliseconds().toString(), 3);
return `${m} : ${s} . ${ms}`;
}
render() {
return (
<div>
<h1>Time: {this.format(this.props.time)}</h1>
<button onClick={this.props.isOn ? this.stop : this.start}>
{ this.props.isOn ? 'Stop' : 'Start' }
</button>
</div>
);
}
}
export default Timer;
Timer Reducer
timer.js
// Initial state for reducer
const initialState = {
isOn: false,
time: 0
};
function timer(state = initialState, action) {
switch (action.type) {
case 'START_TIMER':
return {
...initialState,
isOn: true,
offset: action.offset,
};
case 'STOP_TIMER':
return {
isOn: false,
time: state.time
};
case 'TICK':
return {
...state,
time: state.time + (action.time - state.offset),
offset: action.time
};
default:
return state;
}
}
export default timer;
计时器商店
timerStore.js
import { createStore } from 'redux';
import timer from '../reducers/index.js';
// Create store using the reducer
export const store = createStore(timer);
主要应用程序组件
index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Timer from './components/Timer.js';
import store from './stores/timerStore.js';
const render = () => {
ReactDOM.render(
<Timer
time={store.getState().time}
isOn={store.getState().isOn}
interval={store.getState().interval}
/>,
document.getElementById("app")
);
}
store.subscribe(render);
var interval = null;
store.subscribe(() => {
if (store.getState().isOn && interval === null) {
interval = setInterval(() => {
store.dispatch({
type: 'TICK',
time: Date.now()
});
});
}
if (!store.getState().isOn && interval !== null) {
clearInterval(interval);
interval = null;
}
});
render();
答案 0 :(得分:1)
你有两个不同的问题:
首先,您正在对store变量进行“命名导出”,但该文件的“默认导入”。你需要确保两者匹配。执行export default createStore(timer)
和import store from "./stores/timerStore"
,或执行export const store = createStore(timer)
和import {store} from "./stores/timerStore"
。
其次,你真的不应该对商店进行“手动”订阅。 React-Redux包提供connect
函数,该函数生成容器组件,为您管理订阅和更新过程。
作为补充观察,名为“stores”的文件夹表明您正在创建多个Redux存储。虽然可以这样做,但这不是推荐的方法。
<强>更新强>
而且,看着它,你有第三个问题:<Timer>
组件不会重新渲染,因为它不知道商店已更新。它只会使用ReactDOM.render(<Timer>)
调用中的初始值。
根据我刚才写的评论,你应该把它写成几个不同的组件。一个组件应该连接到Redux存储并使用setInterval
管理计时器逻辑,它应该呈现另一个显示计时器信息的组件。