skip to Main Content

I am trying to create a score keeper component using ReactJs. I am working with vite for local server setup.My score keeper component is working but when i created a form component to add players in the score table. I see no changes in component even though the dev tools show that the prop state is changing.

The Main component where the state lives is:

import { useState } from 'react'
import ScoreKeeper from './ScoreKeeper.jsx';
import EnterFormScore from './EnterFormScore.jsx';

export default function ScoreKeeperInterFace() {
  
  const [players,setPlayers]=useState(["Kartik","Rohit","Virat","Dhoni","Smith"]);
  const addToTable=function (Name) {
    setPlayers([...players,Name])
    
  }
  return (
    <>
    <ScoreKeeper teamNames={players} target={10}/>
    <EnterFormScore add={addToTable}/>

    </>
  )
}

The ScoreKeeper component:

import {useState} from "react"
import {v4 as uuid } from "uuid"
import TeamScore from "./TeamScore.jsx";
import Button from "@mui/material/Button"

export default function ScoreKeeper({teamNames,target=11}){

    const [winner,setWinner]=useState(false);
    const [teamScores,setTeamScores]=useState(teamNames.map(e=>(
            {id:uuid(),Name:e,Score:0}
    )));
    
    const IncreaseScore=(id)=>{

        if(!winner)
        {setTeamScores(curr=>(
            curr.map((e)=>{
                if(e.id===id){
                    if(e.Score===target-1){
                        setWinner({Name:e.Name,id:e.id,winner:true})
                    }
                    return {...e,Score:e.Score<target?++e.Score:e.Score}
                }
                else { return {...e} }
            })
        ))
        }
    }

    const DecreaseScore=(id)=>{

        if(!winner){
            setTeamScores(curr=>(
                curr.map((e)=>{
                    if(e.id===id){
                        return {...e,Score:e.Score===0?0:--e.Score}
                    }
                    else { return {...e} }
                })
            ))
            }
    }

    const Reset=function (){
        setTeamScores(curr=>(
            curr.map((e)=>(
                {...e,Score:0}
            ))
        ))
        setWinner(false)
    }

    return <div className="flex flex-col items-center border rounded-md w-[fit-content] h-auto px-3 pt-3 shadow bg-slate-400">
        {winner.winner && <h1>The Winner is: {winner.Name}</h1>}
        <div className="flex space-x-1">{teamScores.map(e=>(
        <TeamScore inc={()=>{ IncreaseScore(e.id)}} dec={()=>{DecreaseScore(e.id)}} Name={e.Name} Score={e.Score}/>
    ))}
    </div>
    <Button onClick={Reset}>Reset</Button>
    </div>

}

EnterFormScore component is:

import { useState } from "react"
import Button from "@mui/material/Button"

export default function EnterFormScore({add}){

    const [player,setPlayer]=useState("");

    const ControlForm=function (e){
        setPlayer(e.target.value);
    }

    return(
        <div>
            <input id="player_name" value={player} placeholder="Player Name" onChange={ControlForm}/>
            <Button onClick={() =>{ add(player)}}>Add To The Table</Button>
        </div>
    )

}

I have used tailwindCss and material UI also in this component. Please give me some insights i am unable to find a solution to it.

2

Answers


  1. I think the problem is that your ScoreKeeper component is not re-rendering because you’re missing a useEffect inside the component.

    The mapping of the teamNames inside useState is just running on initial render.

    I’d initialize the teamScores state with an empty array and then add the following useEffect to ScoreKeeper component:

    useEffect(() => {
        let newTeamNames = [];
        teamNames.map((name) => {
          if (!teamScores.find((t) => t.Name === name)) {
            newTeamNames.push({ id: uuid(), Name: name, Score: 0 });
          }
        });
        setTeamScores([...teamScores, ...newTeamNames]);
      }, [teamNames, setTeamScores]);
    

    What is it doing?

    It checks if the team is already in the teamScores array, if not it will update the list with the new team. It’s checked by name, would be better to check by id but there is no id (see comment below).

    Some notes to your code:

    • Name variables with camel casing e.g. name or newTeamNames
    • I’d add the uuid in the top-level so you can use it later to identify your teams (not changed in the snippet)

    I haven’t thought about your data structure but fixed your issue.
    You can find the complete demo here

    Full Code below (not working here):

    import {
      useState,
      useEffect
    } from "react";
    import {
      createRoot
    } from "react-dom/client";
    import {
      v4 as uuid
    } from "uuid";
    import Button from "@mui/material/Button";
    
    function EnterFormScore({
      add
    }) {
      const [player, setPlayer] = useState("");
    
      const ControlForm = function(e) {
        setPlayer(e.target.value);
      };
    
      return ( <
        div >
        <
        input id = "player_name"
        value = {
          player
        }
        placeholder = "Player Name"
        onChange = {
          ControlForm
        }
        /> <
        Button onClick = {
          () => {
            add(player);
          }
        } >
        Add To The Table <
        /Button> <
        /div>
      );
    }
    
    function ScoreKeeper({
      teamNames,
      target = 11
    }) {
      const [winner, setWinner] = useState(false);
      const [teamScores, setTeamScores] = useState(
        []
        // teamNames.map((e) => ({ id: uuid(), Name: e, Score: 0 }))
      );
    
      useEffect(() => {
        let newTeamNames = [];
        teamNames.map((name) => {
          if (!teamScores.find((t) => t.Name === name)) {
            newTeamNames.push({
              id: uuid(),
              Name: name,
              Score: 0
            });
          }
        });
        setTeamScores([...teamScores, ...newTeamNames]);
      }, [teamNames, setTeamScores]);
    
      const IncreaseScore = (id) => {
        if (!winner) {
          setTeamScores((curr) =>
            curr.map((e) => {
              if (e.id === id) {
                if (e.Score === target - 1) {
                  setWinner({
                    Name: e.Name,
                    id: e.id,
                    winner: true
                  });
                }
                return { ...e,
                  Score: e.Score < target ? ++e.Score : e.Score
                };
              } else {
                return { ...e
                };
              }
            })
          );
        }
      };
    
      const DecreaseScore = (id) => {
        if (!winner) {
          setTeamScores((curr) =>
            curr.map((e) => {
              if (e.id === id) {
                return { ...e,
                  Score: e.Score === 0 ? 0 : --e.Score
                };
              } else {
                return { ...e
                };
              }
            })
          );
        }
      };
    
      const Reset = function() {
        setTeamScores((curr) => curr.map((e) => ({ ...e,
          Score: 0
        })));
        setWinner(false);
      };
    
      return ( <
        div className = "flex flex-col items-center border rounded-md w-[fit-content] h-auto px-3 pt-3 shadow bg-slate-400" > {
          winner.winner && < h1 > The Winner is: {
            winner.Name
          } < /h1>} <
          div className = "flex space-x-1" > {
            teamScores.map((e) => ( <
              TeamScore id = {
                e.id
              }
              inc = {
                () => {
                  IncreaseScore(e.id);
                }
              }
              dec = {
                () => {
                  DecreaseScore(e.id);
                }
              }
              Name = {
                e.Name
              }
              Score = {
                e.Score
              }
              />
            ))
          } <
          /div> <
          Button onClick = {
            Reset
          } > Reset < /Button> {
            /* <h2>Debug</h2>
                  <pre>
                    teams:
                    {JSON.stringify(teamNames, null, 2)}
                    scores:
                    {JSON.stringify(teamScores, null, 2)}
                  </pre> */
          } <
          /div>
        );
      }
    
    
      // inteface
      const ScoreKeeperInterface = () => {
        const [players, setPlayers] = useState([
          "Kartik",
          "Rohit",
          "Virat",
          "Dhoni",
          "Smith",
        ]);
        const addToTable = function(Name) {
          console.log("add player", Name);
          setPlayers([...players, Name]);
        };
        return ( <
          >
          <
          ScoreKeeper teamNames = {
            players
          }
          target = {
            10
          }
          /> <
          EnterFormScore add = {
            addToTable
          }
          /> <
          />
        );
      }
    
    
      const TeamScore = ({
        Name,
        Score,
        inc,
        dec,
        id
      }) => ( <
        div key = {
          id
        } >
        Name: {
          Name
        } <
        br / >
        Score: {
          Score
        } <
        br / >
        <
        button onClick = {
          inc
        } > Inc. < /button> <
        button onClick = {
          dec
        } > Dec. < /button> <
        hr / >
        <
        /div>
      );
    
    
      function App() {
        return ( <
          div className = "App" >
          <
          ScoreKeeperInterFace / >
          <
          /div>
        );
      }
    
    
      const rootElement = document.getElementById("root");
      const root = createRoot(rootElement);
    
      root.render( <
        StrictMode >
        <
        App / >
        <
        /StrictMode>
      );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
    <script src="
    https://cdn.jsdelivr.net/npm/@mui/[email protected]/umd/material-ui.production.min.js
    "></script>
    <script src="
    https://cdn.jsdelivr.net/npm/@mui/[email protected]/umd/material-ui.production.min.js
    "></script>
    <div id="root"></div>
    Login or Signup to reply.
  2. You can try giving a key value to the component that you want to re render. Because when the component key change, the component will be rendering again.
    Example:

    <div key={refresh}>{"Something to re-render"}  <div/>
    

    if you change the refresh value, it will be re rendering.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search