简体   繁体   中英

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM