0

i'm trying to learn react along with using store. What im trying to do is having 2 components (one main and child) I want to call API for data in main component and pass this data to child component and then render this data in react-chartjs-2 component.

This is my main component (Home.tsx) which calls API for UserData (I receive data from back-end properly as array [].

import React, { useEffect, useState } from "react";
import { History } from "history";
import { connect } from "react-redux";
import { ApplicationState } from "../../store";
import { actionCreators, reducer } from "../../store/auth";
import axios from 'axios';
import 'antd/dist/antd.css'
import { Card, Button } from 'antd';
import BalanceChart from "./child-components/BalanceChart";

type HomeProps = ReturnType<typeof reducer>
    & typeof actionCreators
    & { readonly history: History };

const Home: React.FC<HomeProps> = ({
                                         id,
                                         historicalBalanceValues
                                     }) => {
    const [balanceValue, setBalanceValue] = React.useState([]);

    const getUserRequest = async () => {
        let response = await axios({
            url: `https://localhost:44340/api/users/${id}`,
            method: 'get'
        });
        return response.data;
    };
    useEffect(() => {
        id = sessionStorage.getItem("userId");
        getUserRequest()
            .then(data => {
                setBalanceValue(data.item.balanceValue);

                if (!!data.item.historicalBalances){
                    historicalBalanceValues = data.item.historicalBalances.map(r => r.balanceValue);
                }
            });
    });
    return (
        <div >
            <h1 className='home-header'>Savings</h1>
            <div className='card-container'>
                <Card style={{ width: 400, marginLeft: '75px' }} title={'Total balance'}>
                    <div className='balance-container'>
                        <div className='balance-value'>{balanceValue}</div>
                        <Button className='goal-button' type='primary' shape='round'>Add goal</Button>
                    </div>
                </Card>
                <Card style={{ width: 400, marginLeft: '75px'}} title={'Balance trend'}>
                    <BalanceChart chartData={historicalBalanceValues}></BalanceChart>
                </Card>
            </div>
        </div>
    );
};
const mapStateToProps = (state: ApplicationState) => ({
    id: state.auth.id
});

export default connect(mapStateToProps, actionCreators)(Home);

And this is my second component in which I want to display Line Chart using data I have passed from Home component.

import React, { Component } from 'react';
import { Line } from 'react-chartjs-2';
import axios from 'axios';
import PasswordInput from "../../Login/child-components/PasswordInput";

type BalanceChartState = {
    balanceChartData: []
}
let chartOptionsData = {
    labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
    datasets: [{
        label: 'Label',
        data: this.props.chartData,
        backgroundColor: [
            'rgba(255, 99, 132, 0.2)'
        ],
        borderColor: [
            'rgba(255, 99, 132, 1)',
        ],
        borderWidth: 1
    }]
};

export class BalanceChart extends Component<{chartData: any}, BalanceChartState> {

    componentDidMount() {
        this.setState({balanceChartData: this.props.chartData})
    }

    render() {
        const { balanceChartData } = this.state;
        return (balanceChartData && balanceChartData.length) ?
         <Line data={chartOptionsData}></Line> :  <Line data={[0]}></Line>
    }
}

export default BalanceChart;

The problem is that data I'm passing from one component to the other one is 'undefined'. I think that API call for data happens after the components are rendered, however I don't know how can I achieve this. I want to pass data once components are rendered. I would like to know what is a good practice to achieve this. Thanks

1
  • 2
    Your API call is asyn, so your child component will be rendered with initial value of data. You have 2 options, either you can conditionally render your child component if it has the data from API call or in your child component, check for data, if null, return <></> Commented Apr 4, 2020 at 21:53

2 Answers 2

0

Oh, so I have added a conditional rendering to parent (Home) component. Render looks now like this:

<Card style={{ width: 400, marginLeft: '75px'}} title={'Balance trend'}>
{
   !!historicalBalanceValues ?
   <BalanceChart chartData={historicalBalanceValues}></BalanceChart> : <Spin/>
}
</Card>

So now I have infinite preloader (historicalBalanceValues is undefined), and even tough API call returns me an array with values it does not render my BalanceChart component. I think that conditional rendering is checked only once somehow. How can I achieve this to react on changes?

Sign up to request clarification or add additional context in comments.

Comments

0

I have found solution and I did change both components to class components. I pass data between them using props and I use State to store data I have from back-end inside Home component. Thanks for help!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.