Help with react component rendering after state change

Help with react component rendering after state change
0

#1

I’ve been working on my Pomodoro Clock React back-end for a day or so now and I’m stuck on one small aspect of the challenge which is, how do I get my session textarea to update its value after the decrement button is clicked (that’s all the working code that I have at the moment).

I can get the ‘clock’ to display the decremented session length value as I click the minus button, but the related textarea remains static at the initial value of 25.

My component structure is something like this:

PomodoroComp
  |
  +------SessionComponent (contains the decrement button I'm listening to and the text area)
  +------BreakComponent
  +------MainComp
            |
            +-------TimerComponent
            +-------TimeLeftComp (this one reacts properly to the decremented state)
                        

I’m passing a callback function ‘decSession’ from PomodoroComp down to SessionComponent
and it is definitely calling it back each time the decrement button is clicked but though the state is getting updated, it somehow doesn’t re-render SessionComponent’s textarea with the new value.

If anyone has any thoughts on what I could be doing wrong, I would be very grateful to learn.

Link to my codepen:


#2

Just a stab in the dark, but at around line #158, shouldn’t those be this.props.sessionLength/breakLength as opposed to the code that’s there?


#3

I tried both ways and it doesn’t make a difference that I can see. I think prop. works equally as it is the constructor at this point.


#4

I think the problem is the textarea element. When I change it to a span, it works.

Alternatively, you can feed it the textarea as a value prop:

<textarea id="session-length" className="length" cols="2" rows="1" value={this.props.sessionLength}>
</textarea>

But to me, the span is a better fit, unless you want them to edit the time manually. In that case, I’d use and input.

A couple other things that made me worry in your code:

  decrementSession() {
    this.setState({
        sessionLength: this.state.sessionLength-1
    });
    console.log("decrementSession - length: " + this.state.sessionLength);
  }

Remember that setState is asynchronous so this creates a race condition. Better would be giving that console.log as a second parameter callback to the setState.

Also, I see lines like:

let sDec = document.getElementById("session-decrement");
let sInc = document.getElementById("session-increment");

at the bottom of your code (which doesn’t get used) and the commented out

document.addEventListener('click',this.handleKeyPress);
//...
document.removeEventListener('onClick',this.handleKeyPress);

You shouldn’t be messing with the DOM directly but should be doing it through React. I know it’s a difficult transition when you get into React, but there is almost always a React way to do it. When you start messing with the DOM, you run the risk of breaking React.


#5

Adding to what @kevinSmith mentioned about the textarea, you can ditch the closing textarea tag and just write:

<textarea id="session-length" className="length" cols="2" rows="1" value={this.props.sessionLength} />

Here is what the React documentation says about using the textarea tag.

I agree with Kevin, a span element is the way to go here instead of a textarea element.


#6

Thanks so much! Such a small thing. I will have to remember to refer to the React docs (such as shared by @RandellDawson ) in the future!

For the other concerns you listed.
1- I originally started out with an input field but that was not accepted by the test suite for some reason (I opened an issue about it in github. You can see the failure here:


but weirdly the suite is ok with the textarea having a value attribute. (So I’ll stick to that as my feature list does include the ability to manually type in a session/break length)

2- I didn’t know about the asynchronous setState ! Thanks I’ll look into the method you suggested for logging.

3- the DOM statements to get the session inc/dec buttons is something I planned to use in order to disable these buttons at certain points. I assume that plan is ok?

4- the addEventListener/removeEventListener snippet is directly from the FCC React challenges. I was going to model the handling for the textarea on that snippet. (here’s the link to the FCC challenge that teaches their use:
https://learn.freecodecamp.org/front-end-libraries/react/add-event-listeners
I’ll take a look at the React docs to see if there is a better way to do this. Thank you.


#7
  1. The button element accepts a property disabled. You should have a boolean in state or passed in as props and use that to set that property. That is the React-y way to do it.

  2. Yes, I think the key press events are a unique situation, and works because you are only listening, instead of writing to the DOM. Still, in general it should be avoided. I think dealing with refs is the only other time you access the DOM directly. Everything else - there’s probably a React way to do it.

Yeah, I remember these same trying-to-learn-the-react-way pains. It’s frustrating and you have to turn your head upside-down, but once you learn the React way, there is a certain elegance to it. It just takes a bit.


#8

From that FCC challenge:

React’s synthetic event system is great to use for most interactions you’ll manage on DOM elements. However, if you want to attach an event handler to the document or window objects, you have to do this directly. [emphasis added]

I think the key here is that the keyboard is not managed by React so you need to access it in a non-React way. It’s unfortunate. Randy and I were exploring this the other day and couldn’t find a better method. It’s kind of un-React-y, but for granular keyboard events, it’s how you have to do it for now.


#9

And by Randy, he is referring to me.


#10

I guessed! :smile: Hopefully the nickname is acceptable?


#11

My legal name is Randell, but my friends and close associates know me by Randy.