Search code examples
javascriptreactjsjsxreact-bootstrap

how to send data From one component to another components with context in react?


I need to send selectedCoin state from Company to details.js How do I do this with context?

company component:

import React, { useState, useEffect } from "react";
import { Col, Image, Row } from "react-bootstrap";
import "./Company.scss";

// * api
import { getCoin } from "../services/api";

// *spinner
import Loader from "./Loader";

const Company = () => {
  const [selectedCoin, setSelectedCoin] = useState(null);
  const [coins, setCoins] = useState([]);

  useEffect(() => {
    const fetchAPI = async () => {
      const data = await getCoin();
      setCoins(data);
    };
    fetchAPI();
  }, []);

  const coinId = () => {
    console.log(selectedCoin);
  };

  coinId();

  return (
    <>
      {coins.length > 0 ? (
        coins.map((coin) => (
          <Row
            className={
              selectedCoin === coin.id
                ? "p-2 pe-3 border-top d-flex align-items-center company-list-single-active"
                : "p-2 border-top d-flex align-items-center company-list-single"
            }
            onClick={() => {
              setSelectedCoin(coin.id);
              // console.log(coin.id);
              // console.log(coin.name);
            }}
            key={coin.id}
          >
            <Col xxl="2" xl="2" lg="3" md="3" sm="2" xs="2">
              <Image
                src={coin.image}
                alt={coin.name}
                className="coin-image mx-2"
                fluid
              />
            </Col>
            <Col>
              <span>{coin.name}</span>
            </Col>
          </Row>
        ))
      ) : (
        <Loader />
      )}
    </>
  );
};
export default Company;

details components:

import React, { useState, useEffect } from "react";
import axios from "axios";

const Details = () => {
  const [data, setData] = useState({
    name: "",
    id: "",
  });

  const apiDetails = async () => {
    await axios
      .get(`https://api.coingecko.com/api/v3/coins/${"ethereum"}`)
      .then((r) => {
        // console.log(response);
        setData({
          name: r.data.name,
          id: r.data.id,
        });
        return setData;
      });
  };

  useEffect(() => {
    (async () => {
      const response = await apiDetails();
      setData({
        name: response.data.name,
        id: response.data.id,
      });
    })();
  }, []);

  return (
    <div>
      <h1>{data.name}</h1>
      <h1>{data.id}</h1>
    </div>
  );
};

export default Details;

Solution

  • Well, looks like you're just interested on the code, right? so I'll just drop the code in here.

    Always feel free to move code around and use the syntax that makes sense to you.


    CoinsContext.js

    import React, { createContext, useContext, useState, useEffect } from "react";
    import { getCoin } from "@api";
    
    const CoinsContext = createContext({});
    
    export const CoinsContextProvider = ({ children }) => {
      const [selectedCoin, setSelectedCoin] = useState(null);
      const [coins, setCoins] = useState([]);
    
      useEffect(() => {
        const fetchAPI = async () => {
          const data = await getCoin();
          setCoins(data);
        };
        fetchAPI();
      }, []);
    
      return (
        <CoinsContext.Provider value={{ selectedCoin, setSelectedCoin, coins }}>
          {children}
        </CoinsContext.Provider>
      );
    };
    
    export const useCoins = () => useContext(CoinsContext);
    

    If you want to use the above context on Detail.js and Company.js, you must wrap up the pages with it, that applies to all pages. Usually we wrap the entire App, which means the entire app can use it. There is however a down side. Every time a state inside a context updates, all the pages that are wrapped by that context will update as well, therefore you must assess your application and choose what's best for you.

    That said, wrapping the whole app looks like this:

    index.js

    import React from "react";
    import ReactDOM from "react-dom";
    import App from "./App";
    
    import { CoinsContextProvider } from "./CoinsContext";
    
    ReactDOM.render(
      <React.StrictMode>
        <CoinsContextProvider>
          <App />
        </CoinsContextProvider>
      </React.StrictMode>,
      document.getElementById("root")
    );
    

    Now that we have CoinsContext set up, we can use it on your components.

    Company.js

    import React from "react";
    import { Col, Image, Row } from "react-bootstrap";
    import "./Company.scss";
    
    // *spinner
    import Loader from "./Loader";
    import { useCoins } from "./CoinsContext";
    
    const Company = () => {
      const { coins, selectedCoin, setSelectedCoin } = useCoins();
    
      return (
        <>
          {coins.length > 0 ? (
            coins.map((coin) => (
              <Row
                className={
                  selectedCoin === coin.id
                    ? "p-2 pe-3 border-top d-flex align-items-center company-list-single-active"
                    : "p-2 border-top d-flex align-items-center company-list-single"
                }
                onClick={() => {
                  setSelectedCoin(coin.id);
                  // console.log(coin.id);
                  // console.log(coin.name);
                }}
                key={coin.id}
              >
                <Col xxl="2" xl="2" lg="3" md="3" sm="2" xs="2">
                  <Image
                    src={coin.image}
                    alt={coin.name}
                    className="coin-image mx-2"
                    fluid
                  />
                </Col>
                <Col>
                  <span>{coin.name}</span>
                </Col>
              </Row>
            ))
          ) : (
            <Loader />
          )}
        </>
      );
    };
    export default Company;
    

    Details.js

    import React, { useState, useEffect } from "react";
    import axios from "axios";
    
    import { useCoins } from "./CoinsContext";
    
    const Details = () => {
      const { selectedCoin, coins, setSelectedCoin } = useCoins();
      
      const [data, setData] = useState({
        name: "",
        id: "",
      });
    
      // I recommend you to move this to the context as well
      const apiDetails = async () => {
        await axios
          .get(`https://api.coingecko.com/api/v3/coins/${"ethereum"}`)
          .then((r) => {
            // console.log(response);
            setData({
              name: r.data.name,
              id: r.data.id,
            });
            return setData;
          });
      };
    
      useEffect(() => {
        (async () => {
          const response = await apiDetails();
          setData({
            name: response.data.name,
            id: response.data.id,
          });
        })();
      }, []);
    
      return (
        <div>
          <h1>{data.name}</h1>
          <h1>{data.id}</h1>
        </div>
      );
    };
    
    export default Details;
    

    And we're done! Now you are not just sharing a state (selectedCoin) between your components, but you're also centralizing the fetch coins business logic in your context, which overall is the right thing to do.