Search code examples
javascriptreactjstypescriptimporterrorreact-context

React context will not update (Typescript)


Been stuck here for a week and can't find anything that works. Here is my context file:

import { createContext, useState, useEffect } from 'react';
import * as sm from './StatManagement';
import { PlayerMenu, BossHpBar, BossArea, MainPage } from './MainPage';
//used for the boss hp bar

interface BossContextValue {
    BossHP: number;
    setBossHP: (value: number) => void;  // Function that takes a number
}

export const BossContext = createContext<BossContextValue>({
    BossHP: sm.boss_stats.hp,
    setBossHP: () => { },
});

function ContextManagement() {

    const [BossHP, setBossHP] = useState(sm.boss_stats.hp);
    useEffect(() => {
        //This never triggers 
        console.log('BossHP updated:', BossHP);
    }, [BossHP]);
    return (
        <BossContext.Provider value={{ BossHP, setBossHP }}>
            <PlayerMenu player='' isPlayerTurn={false} />
            <BossHpBar />
        </BossContext.Provider>
    )
}

export default ContextManagement;

What I'm trying to do is create a global state for BossHP. It should update when this button is clicked:

 export const PlayerMenu: React.FC<PlayerMenuProps> = ({ player, isPlayerTurn }) => {
      const { setBossHP } = useContext(BossContext);
  //other elements
                


                        <div className=' grid grid-cols-2 grid-rows-2'>
                            {current_attacks.map(
                                (attack, index) =>
                                    <li key={index} className='atk-btn'>
                                        <button onClick={() => {
                                            const new_hp = pa.PlayerAttack(attack);

                                            setIsAttackAreaShown(true);
                                            setCurrentAttack(attack);
                                          
                                            setBossHP(new_hp);
                                         }}>

                                       //unrelated
                                    }

Then it should update the progress bar here. This component is only linked to PlayerMenu through the context.

 export const BossHpBar = () => {
    const { BossHP } = useContext(BossContext);
    useEffect(() => {
//doesn't update
    console.log('BossHP updated:', BossHP);
    }, [BossHP]);

    console.log("boss hp bar rendered" + BossHP)
    return (
        <progress className={
            'block h-8 glow-ani-border-black boss-prog w-10/12'
        } value={BossHP} max={sm.boss_stats.max_hp}></progress>
    )
}

Solution

  • I created a simple working example based on your code, so when you click the button - the variable in the progress bar is updating. You can check an example in playground here

    import React, { createContext, useState, useContext, useEffect } from 'react';
    
    const sm = {
      boss_stats: {
        hp: 100,
        max_hp: 200,
      },
    };
    
    interface PlayerMenuProps {
      player: string;
      isPlayerTurn: boolean;
    }
    
    export const BossContext = createContext<{
      BossHP: number;
      setBossHP: (value: number) => void;
    }>({
      BossHP: sm.boss_stats.hp,
      setBossHP: () => {},
    });
    
    const PlayerMenu: React.FC<PlayerMenuProps> = ({ player, isPlayerTurn }) => {
      const { setBossHP } = useContext(BossContext);
    
      const handleAttack = () => {
        const new_hp = Math.max(0, Math.floor(Math.random() * 100));
        setBossHP(new_hp);
      };
    
      return (
        <div>
          <button onClick={handleAttack}>Attack</button>
        </div>
      );
    };
    
    const BossHpBar = () => {
      const { BossHP } = useContext(BossContext);
    
      return (
        <div>
          <h2>Boss HP: {BossHP}</h2>
          <progress value={BossHP} max={sm.boss_stats.max_hp}></progress>
        </div>
      );
    };
    
    const ContextManagement = () => {
      const [BossHP, setBossHP] = useState(sm.boss_stats.hp);
    
      useEffect(() => {
        console.log('BossHP updated:', BossHP);
      }, [BossHP]);
    
      return (
        <BossContext.Provider value={{ BossHP, setBossHP }}>
          <PlayerMenu player="" isPlayerTurn={false} />
          <BossHpBar />
        </BossContext.Provider>
      );
    };
    
    export default function App() {
      return <ContextManagement />;
    }