React - BaryoDev/dopaminejs GitHub Wiki

React

dopaminejs-react 1.0.0.

npm install dopaminejs dopaminejs-react

It needs react 18 or newer and dopaminejs 2.3.0 or newer. It ships no TypeScript declarations yet.

What it provides

import { RewardsProvider, useRewards } from 'dopaminejs-react';

function App() {
    return (
        <RewardsProvider config={{ achievements: {} }}>
            <Profile />
        </RewardsProvider>
    );
}

function Profile() {
    const { level, xp, progress, achievements, addXP } = useRewards();

    return (
        <div>
            <p>Level {level}, {xp} XP</p>
            <progress value={progress} max={1} />
            <p>{achievements.length} achievements</p>
            <button onClick={() => addXP(25)}>Earn 25</button>
        </div>
    );
}

RewardsProvider

Prop Meaning
config Passed to RewardSystem. See Reward System.
storage Passed to DataService. See Storage and Saved State.

The provider creates one RewardSystem when it mounts. Later changes to config or storage are ignored. Give the provider a new key to rebuild it.

useRewards

Field Meaning
player The player object
level, xp From the player
progress 0 to 1 inside the current level
achievements Unlocked achievements
addXP(amount, reason?) The reason reaches xp_gained
recordGame(gameName, result)
unlockAchievement(id) Resolves false when the id is unknown or already unlocked
  • Called outside a provider, the hook throws.
  • Until init() resolves, the state is { player: {}, level: 1, xp: 0, progress: 0, achievements: [] }. An action called before the provider's effect has run returns undefined, not a promise. A child's useEffect on first mount runs before it, so call actions from event handlers or check the result. After that it rejects until init() resolves.
  • The state refreshes on xp_gained, level_up, achievement_unlocked and streak_updated.

Without the package

The same thing in your own code, with the core package only:

import { useEffect, useRef, useState } from 'react';
import { RewardSystem, DataService } from 'dopaminejs';

export function useRewardSystem(config) {
    const ref = useRef(null);
    const [state, setState] = useState({ ready: false, level: 1, xp: 0, progress: 0 });

    useEffect(() => {
        const rewards = new RewardSystem(new DataService(), config);
        ref.current = rewards;
        let mounted = true;

        const sync = () => {
            if (!mounted) return;
            setState({
                ready: true,
                level: rewards.player.level,
                xp: rewards.player.xp,
                progress: rewards.getXPForNextLevel().progress
            });
        };

        rewards.init().then(sync);
        const stops = ['xp_gained', 'level_up', 'achievement_unlocked', 'streak_updated']
            .map((event) => rewards.on(event, sync));

        return () => {
            mounted = false;
            stops.forEach((stop) => stop());
        };
    }, []);

    return { ...state, rewards: ref.current };
}

The overlay in React

GameUI and ParticleSystem create DOM in their constructors. Create them in an effect and destroy them in its cleanup:

useEffect(() => {
    const particles = new ParticleSystem();
    const ui = new GameUI(particles);
    return () => {
        ui.destroy();
        particles.destroy();
    };
}, []);
⚠️ **GitHub.com Fallback** ⚠️