Created custom implemtation of atomWithStorage, in order to fix undefined on first render

This commit is contained in:
Martin Berg Alstad 2023-07-22 16:36:08 +02:00
parent aeba6343e3
commit 800ba32064
7 changed files with 51 additions and 29 deletions

View File

@ -1,6 +1,6 @@
import React, {FC} from "react";
import {useAtom, useAtomValue} from "jotai";
import {selectedDiceAtom, thisPlayerAtom} from "../utils/state";
import {getPlayerAtom, selectedDiceAtom,} from "../utils/state";
import {Button} from "./button";
export const AllDice: FC<{ values?: number[] } & ComponentProps> = (
@ -38,7 +38,7 @@ export const Dice: FC<DiceProps> = (
onClick,
}) => {
const thisPlayer = useAtomValue(thisPlayerAtom);
const thisPlayer = useAtomValue(getPlayerAtom);
function handleClick() {
if (onClick && value) {

View File

@ -1,6 +1,6 @@
import React, {FC, MouseEventHandler} from "react";
import {State} from "../game/player";
import {currentPlayerAtom, playersAtom, rollDiceButtonAtom, thisPlayerAtom} from "../utils/state";
import {currentPlayerAtom, getPlayerAtom, playersAtom, rollDiceButtonAtom} from "../utils/state";
import {useAtomValue} from "jotai";
import {Button} from "./button";
import rules from "../game/rules";
@ -17,7 +17,7 @@ const GameButton: FC<GameButtonProps> = (
}) => {
const currentPlayer = useAtomValue(currentPlayerAtom);
const thisPlayer = useAtomValue(thisPlayerAtom);
const thisPlayer = useAtomValue(getPlayerAtom);
const players = useAtomValue(playersAtom);
const activeRollDiceButton = useAtomValue(rollDiceButtonAtom);

View File

@ -1,24 +1,22 @@
import React, {FC, useEffect} from "react";
import {GameComponent} from "../components/gameComponent";
import {useAtomValue} from "jotai";
import {selectedMapAtom, thisPlayerAtom} from "../utils/state";
import {getPlayerAtom, selectedMapAtom} from "../utils/state";
const Game: FC = () => { // TODO gameId in path
const player = useAtomValue(thisPlayerAtom);
const Game: FC = () => {
const player = useAtomValue(getPlayerAtom);
const map = useAtomValue(selectedMapAtom);
useEffect(() => {
console.debug(player);
if (!player) {
// TODO player is undefined on first render, then defined on second render
// window.location.href = "/";
window.location.href = "/";
}
}, [player]);
}, []);
if (player && map) {
return <GameComponent player={player} map={map}/>;
} else {
throw new Error("Player or map is undefined");
return null;
}
};

View File

@ -5,13 +5,13 @@ import Dropdown from "../components/dropdown";
import {Colour, getColours} from "../game/colour";
import {useNavigate} from "react-router-dom";
import {useSetAtom} from "jotai";
import {thisPlayerAtom} from "../utils/state";
import {setPlayerAtom} from "../utils/state";
const Home: FC = () => {
const input = useRef<HTMLInputElement>(null);
const dropdown = useRef<HTMLSelectElement>(null);
const setPlayer = useSetAtom(thisPlayerAtom);
const setPlayer = useSetAtom(setPlayerAtom);
const navigate = useNavigate();
function formHandler(): void {

View File

@ -1,7 +1,7 @@
import React, {FC, Suspense} from "react";
import React, {FC, Suspense, useEffect} from "react";
import {atom, useAtomValue} from "jotai";
import {Button} from "../components/button";
import {selectedMapAtom, thisPlayerAtom} from "../utils/state";
import {getPlayerAtom, selectedMapAtom} from "../utils/state";
import {getData, postData} from "../utils/api";
import {getPacManSpawns} from "../game/map";
import {useNavigate} from "react-router-dom";
@ -13,7 +13,7 @@ const fetchAtom = atom(async () => {
const LobbyPage: FC = () => { // TODO check if player is defined in storage, if not redirect to login
const thisPlayer = useAtomValue(thisPlayerAtom);
const thisPlayer = useAtomValue(getPlayerAtom);
const navigate = useNavigate();
const map = useAtomValue(selectedMapAtom);
@ -35,6 +35,10 @@ const LobbyPage: FC = () => { // TODO check if player is defined in storage, if
}
useEffect(() => {
console.debug(thisPlayer)
})
return (
<>
<Button onClick={createGame}>New game</Button>
@ -50,7 +54,7 @@ export default LobbyPage;
const GameTable: FC<ComponentProps> = ({className}) => {
const data = useAtomValue(fetchAtom);
const thisPlayer = useAtomValue(thisPlayerAtom);
const thisPlayer = useAtomValue(getPlayerAtom);
const navigate = useNavigate();
async function joinGame(gameId: string): Promise<void> {

View File

@ -2,14 +2,14 @@ import React, {FC, FormEvent} from "react";
import {Button} from "../components/button";
import Input from "../components/input";
import {useSetAtom} from "jotai";
import {thisPlayerAtom} from "../utils/state";
import {setPlayerAtom} from "../utils/state";
import Player from "../game/player";
import {useNavigate} from "react-router-dom";
import {postData} from "../utils/api";
const Login: FC = () => {
const setThisPlayer = useSetAtom(thisPlayerAtom);
const setThisPlayer = useSetAtom(setPlayerAtom);
const navigate = useNavigate();
async function handleLogin(e: FormEvent<HTMLFormElement>): Promise<void> {

View File

@ -1,10 +1,9 @@
import Player from "../game/player";
import {atom} from "jotai";
import {atomWithStorage, createJSONStorage} from "jotai/utils";
import {Ghost} from "../game/character";
import {customMap} from "../game/map";
const playerStorage = createJSONStorage<Player | undefined>(() => sessionStorage);
const playerStorage = "player";
/**
* All players in the game.
*/
@ -22,14 +21,35 @@ export const ghostsAtom = atom<Ghost[]>([]);
*/
export const allCharactersAtom = atom(get => [...get(playerCharactersAtom), ...get(ghostsAtom)]);
/**
* The player that is currently using this browser.
* The player that is currently logged in.
*/
export const thisPlayerAtom = atomWithStorage<Player | undefined>("player", undefined, {
...playerStorage,
getItem(key, initialValue): Player | undefined {
const playerProps = playerStorage.getItem(key, initialValue) as PlayerProps | undefined;
return playerProps ? new Player(playerProps) : undefined;
},
const playerAtom = atom<Player | undefined>(undefined);
/**
* Gets the player that is currently logged in. If playerAtom is undefined, then it will try to get the player from session storage.
* @returns An atom representing the player that is currently logged in, or undefined if there is no player logged in.
*/
export const getPlayerAtom = atom(get => {
const atomValue = get(playerAtom);
if (!atomValue) {
const item = sessionStorage.getItem(playerStorage);
if (item) {
const playerProps = JSON.parse(item) as PlayerProps;
return new Player(playerProps);
}
}
return atomValue;
});
/**
* Sets the player that is currently logged in. If player is undefined, then it will remove the player from session storage.
* @param player The player that is currently logged in, or undefined if there is no player logged in.
* @return An atom used to set the player that is currently logged in.
*/
export const setPlayerAtom = atom(null, (get, set, player: Player | undefined) => {
set(playerAtom, player);
if (player)
sessionStorage.setItem(playerStorage, JSON.stringify(player));
else
sessionStorage.removeItem(playerStorage);
});
/**
* All dice that have been rolled.