React - BaryoDev/dopaminejs GitHub Wiki
dopaminejs-react 1.0.0.
npm install dopaminejs dopaminejs-reactIt needs react 18 or newer and dopaminejs 2.3.0 or newer. It ships no TypeScript declarations yet.
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>
);
}| 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.
| 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 returnsundefined, not a promise. A child'suseEffecton first mount runs before it, so call actions from event handlers or check the result. After that it rejects untilinit()resolves. - The state refreshes on
xp_gained,level_up,achievement_unlockedandstreak_updated.
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 };
}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();
};
}, []);