TrevPennington
TrevPennington

Reputation: 475

Does a change in a React components props cause a re-render?

I am building a simple timer as React practice. Right now I am just focusing on getting the seconds to work. When a user inputs a selection in baseSeconds, the timer will stop at that second.

It works if you hardcode a number as a prop instead of passing the state. and I know the props are changing in the component based on the {this.props.baseSeconds} I've outputted as a test. But when I put this.state.baseSeconds as props, the timer keeps on going.

Parent component of Settings:

const baseMin = [];
for (var i=0; i <= 60; i++) {
    baseMin.push(i);
}

const baseSec = [];
for (var i=0; i <= 60; i++) {
    baseSec.push(i);
}

const displayMinutes = baseMin.map((minute) => 
    <option value={minute}>{minute}</option> 
)

const displaySeconds = baseSec.map((second) => 
    <option value={second}>{second}</option>
)

class Settings extends React.Component {
    constructor(props) {
        super();
        this.state = { 
                baseMinutes: 0, 
                baseSeconds: 0, 
                varMinutes: 0, 
                varSeconds: 0 
            };

        this.updateBaseMin = this.updateBaseMin.bind(this);
        this.updateBaseSec = this.updateBaseSec.bind(this);
        this.updateVarMin = this.updateVarMin.bind(this);
        this.updateVarSec = this.updateVarSec.bind(this);
        this.renderTimer = this.renderTimer.bind(this);
    }


    updateBaseMin(event) {
        this.setState({ baseMinutes: event.target.value });
    }

    updateBaseSec(event) {
        this.setState({ baseSeconds: event.target.value });
    }

    updateVarMin(event) {
        this.setState({ varMinutes: event.target.value });
    }

    updateVarSec(event) {
        this.setState({ varSeconds: event.target.value });
    }

    renderTimer(e) {
        e.preventDefault();
        const { baseMinutes, baseSeconds, varMinutes, varSeconds } = this.state;
            return(
                <Timer baseMinutes={baseMinutes} baseSeconds={baseSeconds} varMinutes={varMinutes} varSeconds={varSeconds}/>
            )
    }




    render() {

        const varMin = [];
        for (var i=0; i <= this.state.baseMinutes; i++) {
            varMin.push(i);
        }
        const displayBaseMin = varMin.map((minute) => 
            <option value={minute}>{minute}</option>
        )

        const varSec = [];
        for (var i=0; i <= this.state.baseSeconds; i++) {
            varSec.push(i);
        }
        const displayBaseSec = varSec.map((second) =>
            <option value={second}>{second}</option>
        )

        const { baseMinutes, baseSeconds, varMinutes, varSeconds } = this.state;


    return (

        <Container>
            Settings
            <form onSubmit={this.renderTimer}>BaseTime
                <select onChange={this.updateBaseMin}>
                    {displayMinutes}
                </select>
                :
                <select onChange={this.updateBaseSec}>
                    {displaySeconds}
                </select>
                VarTime +-
                <select onChange={this.updateVarMin}>
                    {displayBaseMin}
                </select>
                :
                <select onChange={this.updateVarSec}>
                    {displayBaseSec}
                </select>
                <input type="submit" value="Submit" />
            </form>

            <p>{this.state.baseMinutes}, {this.state.baseSeconds}, {this.state.varMinutes}, {this.state.varSeconds}</p>

            <div>{this.renderTimer}</div>
            <Timer baseMinutes={baseMinutes} baseSeconds={this.state.baseSeconds} varMinutes={varMinutes} varSeconds={varSeconds}/>
        </Container >
    )
}
}

export default Settings

child component of Timer:

class Timer extends React.Component {
    constructor(props) {
        super(props);
        this.state = { 
                minutes: 0,
                seconds: 0,
                baseSeconds: this.props.baseSeconds
        }

    this.go = this.go.bind(this);
    this.stop = this.stop.bind(this);
    this.reset = this.reset.bind(this);
    }

    go = () => {

        this.timer = setInterval(() => {

            if ((this.state.seconds) === (this.props.baseSeconds)) {

                clearInterval(this.timer);

            } else {
                this.setState({ seconds: this.state.seconds + 1 })
                console.log(this.state.baseSeconds)
            }
        }, 1000)



    }

    stop = () => {
       clearInterval(this.timer);
    }

    reset = () => {
        this.setState({ minutes: 0, seconds: 0 })
    }


    render() {


        return (
            <div>
                <button onClick={this.go}>start</button>
                <button onClick={this.stop}>stop</button>
                <button onClick={this.reset}>reset</button>
                <p>{this.props.baseMinutes}:{this.props.baseSeconds}</p>

                <p>{this.state.minutes}:{this.state.seconds}</p>
            </div>

        )
    }
}

export default Timer

Upvotes: 2

Views: 1936

Answers (1)

Firealem Erko
Firealem Erko

Reputation: 357

Yes, A change in props causes a re-render by default. BUT in your case, in the child component, the initial state (baseSeconds) is based on a prop (this.props.baseSeconds) which is not recommended. The constructor runs only once ( when the component mounts ) and any update on the baseSeconds props after that won't be detected as a result. You can use the props directly inside render without using the local state. if you must update the local state using props, the recommended approach would be to use getDerivedStateFromProps lifecycle method.

Upvotes: 3

Related Questions