简体   繁体   中英

When state is updated, component does not re-render with updated state (REACT)

Ok so let me just start with this code probably looks like a mess but the problem is pretty simple I think.

So whenever I change the state in the parent of the below 'TitleCards' child component, the state changes in the child component too but the portfolioTotal value in the render method of the 'TitleCards' component does not recalculate based on the changed state. I think it has something to do with the async function, but I'm not sure.

Any ideas? All I want is when the state changes in the child component (which I can see with dev tools that it is changing), for the component to automatically re-render/recalculate portfolioTotal with the changed state.

import "./StockCard.css";
import { IEX } from "./IEX.js";
import moment from 'moment';
import Folder from "./folder.png";
import MoneyBag from "./money-bag.png";
import Handshake from "./hand-shake.png";
import add from "./add.png";
import NumberFormat from 'react-number-format';

const LOGO_API = "https://eodhistoricaldata.com/img/logos/US/";

class TitleCards extends Component {
    constructor(props){
        super(props)
        this.arr = this.totalPortfolio();
        this.state ={
            portfolioTotal: '',
            WhenThisChangesShouldntTheComponentReRender: this.props.info,
        }
        this.totalPortfolio = this.totalPortfolio.bind(this);
        this.getIntradayPrice = this.getIntradayPrice.bind(this);

    }

    static defaultProps = {
        TitleCardInfo: [
          { name: "Portfolio", ticker: "DIA"}, 
          { name: "Total Return", ticker: "SPY"}, 
          { name: "Positions Held", ticker: "IWM"}, 
          { name: "Add Position", ticker: "Gold"},],
      }

    async getIntradayPrice(tick) {
        const resp = await fetch(`${IEX.base_url}/stock/${tick}/intraday-prices?chartLast=1&token=${IEX.api_token}`);
        return resp.json();
      }

    async totalPortfolio() {
        const { info } = this.props;
        const respPromises = info.map(({ tick }) => this.getIntradayPrice(tick));
        const respArrays = await Promise.all(respPromises);
        const result = respArrays.reduce((acc, val, index) => acc + val[0].close * info[index].amtPurch, 0);
        return result;
      }
      
    componentDidMount() {
        this.totalPortfolio()
          .then(portfolioTotal => {
            this.setState({
              portfolioTotal
            });
          })
        .catch(error => {
            // add any required error handling/messaging here
        });
    }

    render(){  
        const { portfolioTotal } = this.state;
        return(
            <div className="positioning">
                <div className="StockCardTitle">
                    <img src={Folder} className="StockCardTitle-image" />  
                    {portfolioTotal}
                </div>
            </div>
        )
    }
}
export default TitleCards;

Parent Component where state changes and is passed down to TitleCard Component, if its of any help.

import React, { Component } from 'react';
import StockCard from "./StockCard";
import TitleCards from "./TitleCards";
import { IEX } from "./IEX.js";

const LOGO_API = "https://eodhistoricaldata.com/img/logos/US/";

class UserInput extends Component {
    constructor(props){
        super(props)
        this.state ={
            tickerName: "",
            sAmtPurch: "",
            sPurchPrice: "",
            sDatePurch: "",
            tickerList: [
                { tick: "AAPL", amtPurch: 50, purchPrice: 10, datePurch: "01/01/2021"},
                { tick: "GOOG", amtPurch: "40", purchPrice: "10", datePurch: "01/01/2021"},
                { tick: "TSLA", amtPurch: "40", purchPrice: "10", datePurch: "01/01/2021"},
                { tick: "J", amtPurch: "40", purchPrice: "10", datePurch: "01/01/2021"},
                { tick: "AMZN", amtPurch: "40", purchPrice: "10", datePurch: "01/01/2021"},
                { tick: "FB", amtPurch: "40", purchPrice: "10", datePurch: "01/01/2021"},
                { tick: "BABA", amtPurch: "40", purchPrice: "10", datePurch: "01/01/2021"},
                { tick: "JNJ", amtPurch: "40", purchPrice: "10", datePurch: "01/01/2021"},
                { tick: "JPM", amtPurch: "40", purchPrice: "10", datePurch: "01/01/2021"},
                { tick: "XOM", amtPurch: "30", purchPrice: "10", datePurch: "01/01/2021"},
                ],
            data4: {}
        }
        this.handler = this.handler.bind(this)
    }

    handleSubmit = (event) => {
        event.preventDefault()
        // Initialize state into variables 
        const tickerToBeAdded = { tick: this.state.tickerName, amtPurch: this.state.sAmtPurch, purchPrice: this.state.sPurchPrice, datePurch: this.state.sDatePurch};
        const tickerList = this.state.tickerList;
        
        // Apend tickerList with inputed ticker
        const length = tickerList.length;
        tickerList[length] = tickerToBeAdded;
        this.setState({
            tickerList: tickerList
        })
    }
    handleInputChange = (event) => {
        event.preventDefault()
        // Store Input in State
        this.setState({
            [event.target.name]: event.target.value
        })
    }

    componentDidMount() {
    // query the api
    const url4 = `${IEX.base_url}/stock/${this.state.tickerList.tick}/intraday-prices?chartLast=1&token=${IEX.api_token}`;
    fetch(url4)
      .then((response) => response.json())
      .then((data4) => {
        this.setState({
          data4: data4[data4.length - 1],
        });
      });
    }

    render () {

        return (
            <div>
                <form onSubmit={this.handleSubmit}>
                    <p><input type='text' placeholder='Stock Ticker' name='tickerName' onChange={this.handleInputChange}/></p>
                    <p><input type='text' placeholder='Shares' name='sAmtPurch' onChange={this.handleInputChange}/></p>
                    <p><input type='text' placeholder='Purchase Price' name='sPurchPrice' onChange={this.handleInputChange}/></p>
                    <p><button>Send Message</button></p>
                </form>

                <div className="Charts">
                        <TitleCards info={this.state.tickerList}/>
                </div>
            </div>
        )
    }
}

export default UserInput;

componentDidMount only fires once per mount. If you want to run something every time the component re-renders, you need componentDidUpdate . Take a look at this lifecycle diagram for what method to use.

Be careful when using componentDidUpdate with this.setState . If you blindly update state every cycle in componentDidUpdate, you'll trigger an infinite loop. (See this answer for details.)

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