Search code examples
reactjsreact-state-management

Show data from a card component to a Detail Component using useContext


I´m trying to update the Detail component data whenever I select a card component. I´m using useContext hook but I´m stuck at this point and I don´t know how to do this. Could anybody please help?.

enter image description here

context.js

import { createContext, useContext, useEffect, useState } from "react";
import api from "./api/players"

const APIcontext = createContext();

export function Provider({children}){

    const [players, setPlayers] = useState([]);
    const [currentPlayer, setCurrentPlayer] = useState(null)

  useEffect(() => {

    const fetchPlayers = async () => {

      try{
        const response = await api.get('/players');
        setPlayers(response.data)         
        
      }

      catch(err){

        if(err.response){
          console.log(err.response.data)
        }else{
           
          console.log(`Error: ${err.message}`)
        }
          
      }
    }
    
    fetchPlayers()
  },[])

  
 const UpdateDetails = () =>{
   
 
 }
    return(
        <APIcontext.Provider value={{players, currentPlayer,UpdateDetails}}>{children}</APIcontext.Provider>
    )
}



export default APIcontext;

This is the Detail Component where I want to display the data, whenever a card is selected on click.

Details.jsx

import React from "react";
import Button from "react-bootstrap/Button";
import Card from "react-bootstrap/Card";
import {useContext} from "react";
import APIcontext from "../context";

function Details() {

   const {players} = useContext(APIcontext)
  return (
    <Card  style={{width:"80%", marginRight:"20px"}}>
      <Card.Header className="text-center"><h1>Details</h1>
      </Card.Header>
      <Card.Body className="px-5">
       <h4>{players.realName}</h4>
       <h4>{players.realName}</h4>
       <h4>{players.assets}</h4>
        <Button variant="primary">Go somewhere</Button>
      </Card.Body>
    </Card>
  );
}

export default Details;

This is the Card component that displays the data from the players. By clicking on one of them the Details component should be updated.

Card Component

    [enter code here
 import React, { useEffect, useState, useContext } from "react";
import Card from "react-bootstrap/Card";
import APIcontext from "../context";

function Cards({}) {
  const { players } = useContext(APIcontext);
  console.log("players", players);

  const { UpdateDetails} = useContext(APIcontext)

  return (
    <>
      {players &&
        players.map((player) => (
          <Card
            key={player.id}
            className="mt-4 mx-2"
            style={{
              display: "flex",
              width: "12rem",
              height: "9rem",
              whiteSpace: "nowrap",
              overflow: "hidden",
              textOverflow: "ellipsis",
            }}
          >
            <Card.Body onClick={UpdateDetails}>
              <Card.Title>{player.realName}</Card.Title>
              <Card.Subtitle className="mb-2 text-muted">
                {player.playerName}
              </Card.Subtitle>
              <Card.Text>{player.asset}</Card.Text>
            </Card.Body>
          </Card>
        ))}
    </>
  );
}

export default Cards;][2]

App.js

import "./App.css";
import "bootstrap/dist/css/bootstrap.min.css";
import Card from "./components/Card";
import Details from "./components/Details";
import { Container, Row, Col } from "react-bootstrap";
import Controls from "./components/Controls";
import { useEffect, useState } from "react";
import { Provider } from "./context";

function App() {
  

  return (

    <div className="App">
      <Provider>
      <Container className="p-3 d-flex flex-row justify-content-between">
        <Container className="p-5 d-flex flex-row mb-4 bg-light rounded-3">
          <Details />
          <Controls />
        </Container>
      </Container>

      <Container className=" mr-5 mb-4 bg-light rounded-3 " style={{width:"65%", float:"left", marginLeft:"10%"}}>
        <Container className="d-flex flex-row flex-justify-content-center flex-wrap mr-5 mb-4 bg-light rounded-3" >
        <Card className="mr-2"/>        
        </Container>
      </Container>
      </Provider>
    </div>
  );
}

export default App;

Solution

  • Context implementation is easy, you only need to understand its flow and how to update the root values.

    You have done everything right, and I believe all that is left is for you to update the Details component on click of the Card component. Here's how you could go about it.

    1. In the Card component, where you've handled the onClick event:
      <Card.Body onClick={UpdateDetails}>, change it to: <Card.Body onClick={() => UpdateDetails(player)}>
      This will help us send the data of the card we want to show in the Details component.
    2. Inside the context file, in the UpdateDetails method, collect the argument passed while calling the function and set the currentPlayer accordingly like this:
      const UpdateDetails = (player) => setCurrentPlayer(player) Here, the context will get updated and wherever you use the currentPlayer value will receive the new data.
    3. You also need the currentPlayer state inside Details file, import it along with players state:
      const {players, currentPlayer} = useContext(ApiContext)
      Now you can use currentPlayer.name or any other key from the player object.

    Hope this helps. Please accept this answer if it does! Thank you!