0

I am attempting to update a table with with the response from an API. The response comes to one component and the table lies in another. I can see that I am successfully updating the context in the component but it doesn't seem to pass to the others. I have tried updating context and then passing it via prop drilling but that didn't work either. How do I get the component that contains the tables to listen to changes in the context? The code for my components is below:

Component in which the context is updated:

import React, { useState, useContext } from 'react';
import { Card } from 'react-bootstrap';
import axios from 'axios';

import Cd from './types/Cd';
import Bond from './types/Bond';
import AssetContext from '../../context/AssetContext';

const fixedCard = (props) => {
const [fixed, setFixed] = useContext(AssetContext);
const [bond, setBond] = useState(false);

const assets = fixed;

const getResult = (event) => {
    const form = event.currentTarget;
    event.preventDefault();
    event.stopPropagation();

    let principle = form.principle.value;
    const iLength = form.investmentLength.value / 12;
    let compound = form.compound.value
    let rate = form.rate.value;
    let amount;

    if (form.amount) {
        amount = form.amount.value;
        principle = principle * amount;
    }

    if (isNaN(rate) || rate < 0) {
        alert("As much fun as it would be to calculate the result with \"" + rate + "\", it can't be done.  Please enter a valid number");
    } else {

        if (compound === "Monthly") {
            compound = 12;
        } else if (compound === "Annually") {
            compound = 1;
        } else if (compound === "Quarterly") {
            compound = 4;
        } else {
            compound = 365;
        }

        const headers = {
            'Content-type': 'application/json',
            'Access-Control-Allow-Origin': 'localhost:3000/',
            'Access-Control-Allow-Methods': 'POST',
        }

        const body = {
            principle: principle,
            interestRate: rate,
            length: iLength,
            compoundFrequency: compound
        }

        axios.post("http://localhost:8080/compound-calculator/savings", body, { headers })
            .then(res => {
                assets.push(res.data);
                setFixed(assets);
                fixed.map(asset => console.log(asset));
            });

    }
}

const linkCursor = { cursor: 'pointer' }

const toggleBond = () => {
    setBond(true);
}

const toggleCert = () => {
    setBond(false);
}

return (
    <Card body>
        <Card.Title>
            Compount interest calculator
        </Card.Title>
        <Card.Link onClick={() => toggleCert()} style={linkCursor}>Certificate</Card.Link>
        <Card.Link onClick={() => toggleBond()} style={linkCursor}>Bond</Card.Link>
        <hr></hr>
        <br></br>
        {!bond && <Cd getResult={getResult} />}
        {bond && <Bond getResult={getResult} />}
        <br></br>
    </Card>
    );
}

export default React.memo(fixedCard);

Root component:

import React, { useState, useEffect } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import './GrowthCalculator.css';
import { Container, CardDeck } from 'react-bootstrap';

import FixedCard from '../components/fixedAssets/FixedCard';
import StockCard from '../components/StockCard';
import ResultCard from '../components/ResultCard';
import Navigation from '../components/Navigation';
import AssetContext from '../context/AssetContext.js';
import HowTo from '../components/HowTo';

const growthCalculator = (props) => {
    const [fixedState, setFixedState] = useState([]);
    const [stockState, setStockState] = useState([]);

    const assets = [fixedState, setFixedState, stockState, setStockState];

    return (

        <main>
            <AssetContext.Provider value={[...assets]}>
                <Container>
                    <br></br>
                    <Navigation />
                </Container>
                <br></br>
                <CardDeck>
                    <FixedCard /> //context updated here
                    <StockCard />
                    <ResultCard />  //trying to pass it here
                </CardDeck>
                <br></br>
                <HowTo />
            </AssetContext.Provider>
        </main>
    );
}

export default React.memo(growthCalculator);

Component I'm trying to pass it to (the layout here is still a work in progress):

import React, { useContext, useState } from 'react';
import { Card, Row, Col, Table } from 'react-bootstrap';

import AssetContext from '../context/AssetContext';

const resultCard = (props) => {
    // eslint-disable-next-line
    const [fixed, setFixed, stocks, setStocks] = useContext(AssetContext);

    return (
        <Card body>
            <Card.Title>
                Result card
            </Card.Title>
            <br></br>
            <hr></hr>
            <br></br>
            <section>
                <Row>
                    <Col>
                        <Table striped borderless hover size="sm" variant="secondary" responsive>
                            <thead>
                                <tr>
                                    <th>
                                        Compounding assets
                                </th>
                                </tr>
                            </thead>
                            <tbody>
                              //This doesn't render anything when fixed is updated in FixedCard.js
                                {fixed.map(asset => (
                                    <tr>
                                        <td>
                                            ${asset.principle}
                                        </td>
                                        <td>
                                            total: ${asset.endValue}
                                        </td>
                                    </tr>))
                                }
                            </tbody>
                        </Table>
                    </Col>
                    <Col>
                        <Table striped borderless hover size="sm" variant="secondary" responsive>
                            <thead>
                                <tr>
                                    <th>
                                        Stocks
                                </th>
                                </tr>
                            </thead>
                            <tbody>
                                <tr>
                                    <td>
                                        Ticker
                                </td>
                                    <td>
                                        Fair price
                                </td>
                                    <td>
                                        Discount price
                                </td>
                                </tr>
                            </tbody>
                        </Table>
                    </Col>
                </Row>
            </section>
        </Card>
    );
}

export default resultCard;

To reiterate my question, How do I get resultCard to listen for changes in AssetContext?

Thanks

J-Daniel-S
  • 47
  • 8

2 Answers2

0

To listen to changes and then implementing a change, what i do is use useeffect where i have to listen to the change and make changes. For example you can pass the incoming response from the api as props to the component. In the child component you can use useEffect like this

  useEffect(() => {
       code here or change states, whichever you need to do
        })

    }, [props.responseIncomingFromParent])

Let me know if this works...

Umer Qureshi
  • 115
  • 1
  • 9
  • I must be doing it wrong because I can't get the other components to re-render/notice when the context is updated whether I use useContext or prop drill it. I also tried using useRef (I may give that another shot) but nothing changes even though the state us successfully updating. I would rather not abandon the child component if I don't have to to keep my components as modular as possible but it looks like I may not get the choice. – J-Daniel-S Aug 30 '20 at 05:46
  • Thanks very much for the suggestion btw :) – J-Daniel-S Aug 30 '20 at 05:46
  • Did u find a solution? and if i may ask, what are you doing here? const [fixed, setFixed, stocks, setStocks] = useContext(AssetContext); – Umer Qureshi Sep 02 '20 at 13:02
  • if you are destructuring it i dont think thats the right approach to do that? I think this is the approach you should be using. Well do correct me if i am wrong in my assumption. I am a beginner as well. Try this: const assetContext = useContext(AssetContext); const {fixed, setFixed, stocks, setStocks} = assetContext; – Umer Qureshi Sep 02 '20 at 13:03
  • I posted the question again after a few changes. The problem was that I was trying to copy the state directly instead of spreading it. I created another constant and then spread the state array into it --> const arr = [...fixed]. This worked – J-Daniel-S Sep 03 '20 at 03:03
0

The answer given in another question was that I needed to spread the context variable into another constant and then use that one.

const fixedArr = [...fixed];

/* the code above*/

fixedArr.map(asset => {
    //the code you see above
    }
);
J-Daniel-S
  • 47
  • 8