Pavel Vasiluk
Pavel Vasiluk

Reputation: 317

React: componentWillReceiveProps is not triggered when props change

I am new to react and its life-cycles, so currently following some tutorials and I am stuck with a problem that componentWillReceiveProps life-cycle method is not working the way I expect. The thing is that in App component I am passing prop isActive to Card component, and it is changing its value when input checkbox checked/unchecked, so I expect componentWillReceiveProps life-cycle method to be triggered. However, this is not working at all. Maybe anything you can advice me on that case? As well as I am open for the best practice advice. Thank you for your time in advance.

Components code:

//App.js
import React, {Component} from 'react';
import Ticker from "./Ticker/Ticker";
import currencies from './currencies';
import Card from "./Card/Card";
import uuid from "uuid";
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css';

class App extends Component {

    state = {
        activePairs: []
    };

    handleCheckbox = (rateId, event) => {

        const {checked} = event.target;

        this.setState(({activePairs}) => {

            let pairs = [...activePairs];

            if (checked) {
                if (!pairs.includes(rateId)) {
                    pairs.push(rateId);
                }
            } else {
                let index = pairs.findIndex(rate => rate === rateId);
                pairs.splice(index, 1);
            }

            return {
                activePairs: pairs
            };
        });
    };

    render() {

        return (
            <div className="App">
                <Ticker handleCheckbox={this.handleCheckbox.bind(this)}/>

                <div className="container">
                    <div className="row">
                        {currencies.map(pair => <Card key={"card-" + uuid.v4()} currency={pair}
                                                      isActive={this.state.activePairs.includes(pair)}/>)}
                    </div>
                </div>
            </div>
        );
    }
}

export default App;

//Card.js
import React, {Component} from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import '../App.css';

class Card extends Component {

    state = {
      value: 0
    };

    componentWillReceiveProps(nextProp) {
        console.log(nextProp);

        if (!this.props.isActive && nextProp.isActive) {
            this.updateExchangeRate();
            this.interval = setInterval(this.updateExchangeRate, 3000);
        } else if (this.props.isActive && !nextProp.isActive) {
            clearInterval(this.interval);
            this.setState({
                value: 0
            })
        }
    }

    updateExchangeRate = () => {
        return fetch(`https://www.cbr-xml-daily.ru/daily_json.js`).then(r => r.json()).then(res => {

            let exRate = res["Valute"][this.props.currency.toUpperCase()]['Value'] + (Math.random() * (0.99 - 0.01 + 1) + 0.01);
            let maximum = exRate + 5.00;
            let minimum = exRate - 5.00;

            this.setState({
                value: (Math.floor(Math.random() * (maximum - minimum + 1)) + minimum).toFixed(2)
            });
        });
    };

    render() {
        return (
            <div className="col-md-3 col-sm-6 mb-3">
                <div className="card text-center text-white bg-info">
                    <div className="card-header bg-info">{this.props.currency.toUpperCase() + " to RUB"}</div>
                    <div className="card-body">
                        <h5 className="card-title">Current exchange pair:</h5>
                        <p className="card-text">{this.state.value}</p>
                    </div>
                </div>
            </div>
        );
    }
}

export default Card;

//Ticker.js
import React, {Component} from 'react';
import currencies from "../currencies";

export default class Ticker extends Component {

    state = {
        currencies: currencies
    };

    render() {
        return (
            <div id="wrapper">
                <div id="sidebar-wrapper">
                    <ul id="sidebar-ul" className="sidebar-nav">
                        {this.state.currencies.map(currency => {
                            return <li key={currency}>
                                <input id={currency + "-input"} type="checkbox" onChange=
                                    {
                                        this.props.handleCheckbox.bind(this, currency)
                                    }/>
                                <label htmlFor={currency + "-input"} className="text-info"
                                       role="button"> {currency.toUpperCase()} rate</label>
                            </li>
                        })}
                    </ul>
                </div>
            </div>
        );
    }
}

//currencies.js
export default ["aud", "azn", "gbp", "bgn", "usd", "eur"];

Upvotes: 1

Views: 657

Answers (1)

Pavel Vasiluk
Pavel Vasiluk

Reputation: 317

Well, I finally found what was causing the problem here. In App component I was using uuid module as a key prop for every Card component, so because of it that was always rendering a new Card component each time isActive props were updating.

Solution: use a constant id instead as a key prop.

Upvotes: 1

Related Questions