0

I'm trying to make a timer in React but, I have a problem. The error that I am getting is this:

Uncaught TypeError: Cannot read property 'seconds' of undefined'.

I have tried to understand but, I can't find the problem.

Can someone help me ?

Here is the fiddle : https://jsfiddle.net/q806zeps/17/

Thank you.

I think the problem is here because if I comment the render, it's ok.

<TimerInput 
    value={this.state.seconds} 
    handleChange={this.handleChange} 
/> 
<Minuteur 
    seconds={600} 
    libelle="Pâtes"
/>```

Geo
  • 2,321
  • 2
  • 12
  • 19
Tube Yu
  • 11
  • 1
  • 5
  • That error means you are trying to read the `seconds` field of something before it has been defined. There's nothing there yet. This problem arises [a lot](https://stackoverflow.com/questions/8004617/javascript-cannot-read-property-bar-of-undefined). (BTW, this is a problem that cannot arise in some static typed languages, such as Haskell, because they make it impossible to create a thing that has no value.) – Jeffrey Benjamin Brown Nov 17 '18 at 16:06

4 Answers4

2

In addition to Levi's answer (as I'm not allowed to comment yet):

Your TimerInput component is trying to read prop seconds (not value), so your render line for it should look like:

<TimerInput seconds={this.state.seconds} handleChange={this.handleChange} />
MrAleister
  • 1,551
  • 1
  • 10
  • 15
1

The problem is that you are trying to access the state and methods of the Minuteur component from the parent component App.

const App = () => {
    return (
    <div>
      <TimerInput value={this.state.seconds} handleChange={this.handleChange} />
      <Minuteur seconds={600} libelle="Pâtes"/>
    </div>
  )
}

this.state.seconds and this.handleChange refer to attributes on the Minuteur component. Since it seems like TimerInput and Minuteur need to share some state, you have a couple options.

  1. Combine the TimerInput and Minuteur components into one so they have the same state.
  2. Create a wrapping component that contains both TimerInput and Minuteur and move the shared state (e.g. state.seconds) to that component.

The approach for option 2 would look roughly like this:

class Wrapper extends React.Component {
  constructor(props) {
    super(props)
    this.state = { 
      seconds: 0
    }

    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    this.setState({
      seconds: event.target.value
    })
  }

  render() {
    return (
      <div>
        <TimerInput value={this.state.seconds} handleChange={this.handleChange} />
        <Minuteur seconds={this.state.seconds} libelle="Pâtes"/>
      </div>
    )
  }


const App = () => {
  return (
    <div>
      <Wrapper />
    </div>
  )
}
Levi Payne
  • 327
  • 2
  • 10
  • Thanks but I have this error now... react-dom.development.js:49 Uncaught Error: Wrapper(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.https://jsfiddle.net/q806zeps/26/ – Tube Yu Nov 17 '18 at 16:20
  • I can't see your error, but see MrAleister's answer for another correction you need to make. I just updated my answer with it. – Levi Payne Nov 17 '18 at 16:22
  • I missed a return statement in `render()`. Answer has been updated. – Levi Payne Nov 17 '18 at 16:25
0

Just add return to the Wrapper's render:

render() {
  return(
    <div>
      <TimerInput seconds={this.state.seconds} handleChange={this.handleChange} />
      <Minuteur seconds={this.state.seconds} libelle="Pâtes"/>
    </div>
  )
}
MrAleister
  • 1,551
  • 1
  • 10
  • 15
0

Quick and dirty solution - set initial Wrapper seconds state to anything but 0 (like 600 you had in your original fiddle):

this.state = { 
  seconds: 600
}
MrAleister
  • 1,551
  • 1
  • 10
  • 15