ReactJS:未捕获TypeError:无法读取undefined(...)

时间:2016-11-29 18:47:22

标签: reactjs redux react-redux

我正在尝试使用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();

1 个答案:

答案 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管理计时器逻辑,它应该呈现另一个显示计时器信息的组件。