Make useLocalStorage actually work

This commit is contained in:
Gwilym Inzani 2023-07-05 11:59:32 +01:00 committed by Corwin
parent 472d641cfc
commit 683afbf133
No known key found for this signature in database
3 changed files with 138 additions and 118 deletions

View file

@ -1,5 +1,5 @@
import React, { useState } from "react";
import { Mgba } from "./mgba";
import { useRef, useState } from "react";
import { Mgba, MgbaHandle } from "./mgba";
import { BindingsControl, DefaultBindingsSet, Bindings } from "./bindings";
import { styled } from "styled-components";
import { useOnKeyUp } from "./useOnKeyUp.hook";
@ -16,24 +16,28 @@ const VolumeLabel = styled.label`
margin-bottom: 20px;
`;
const CloseButton = styled.button`
const ActionButton = styled.button`
width: 100%;
margin-top: 20px;
`;
function App() {
const [volumeState, setVolume] = useState(1.0);
const [bindingsState, setBindings] = useState(DefaultBindingsSet());
const { volume, bindings } = useLocalStorage(
{ volume: volumeState, bindings: bindingsState },
const [{ volume, bindings }, setState] = useLocalStorage(
{ volume: 1.0, bindings: DefaultBindingsSet() },
"agbrswebplayer"
);
const setVolume = (newVolume: number) =>
setState({ volume: newVolume, bindings });
const setBindings = (newBindings: Bindings) =>
setState({ volume, bindings: newBindings });
const [paused, setPaused] = useState(false);
const [showBindings, setShowBindings] = useState(false);
const mgbaRef = useRef<MgbaHandle>(null);
useOnKeyUp("Escape", () => {
setShowBindings(!showBindings);
});
@ -48,9 +52,11 @@ function App() {
volume={volume}
setVolume={setVolume}
hide={() => setShowBindings(false)}
restart={() => mgbaRef.current?.restart()}
/>
)}
<Mgba
ref={mgbaRef}
gameUrl="/game.gba"
volume={volume}
controls={bindings.Actual}
@ -67,6 +73,7 @@ function BindingsWindow({
volume,
setVolume,
hide,
restart,
}: {
bindings: Bindings;
setBindings: (b: Bindings) => void;
@ -74,6 +81,7 @@ function BindingsWindow({
volume: number;
setVolume: (v: number) => void;
hide: () => void;
restart: () => void;
}) {
return (
<BindingsDialog open onClose={hide}>
@ -97,7 +105,8 @@ function BindingsWindow({
setBindings={setBindings}
setPaused={setPaused}
/>
<CloseButton onClick={hide}>Close</CloseButton>
<ActionButton onClick={restart}>Restart</ActionButton>
<ActionButton onClick={hide}>Close</ActionButton>
</BindingsDialog>
);
}

View file

@ -1,4 +1,11 @@
import { FC, useEffect, useRef, useState } from "react";
import {
FC,
forwardRef,
useEffect,
useImperativeHandle,
useRef,
useState,
} from "react";
import mGBA from "./vendor/mgba";
import { KeyBindings } from "./bindings";
import { styled } from "styled-components";
@ -28,7 +35,12 @@ const MgbaCanvas = styled.canvas`
max-height: 100%;
`;
export const Mgba: FC<MgbaProps> = ({ gameUrl, volume, controls, paused }) => {
export interface MgbaHandle {
restart: () => void;
}
export const Mgba = forwardRef<MgbaHandle, MgbaProps>(
({ gameUrl, volume, controls, paused }, ref) => {
const canvas = useRef(null);
const mgbaModule = useRef<Module>({});
@ -112,5 +124,12 @@ export const Mgba: FC<MgbaProps> = ({ gameUrl, volume, controls, paused }) => {
}
}, [gameLoaded, paused]);
useImperativeHandle(ref, () => {
return {
restart: () => mgbaModule.current.quickReload(),
};
});
return <MgbaCanvas ref={canvas} />;
};
}
);

View file

@ -1,36 +1,28 @@
import { useRef, useLayoutEffect, useEffect } from "react";
import { useCallback, useEffect, useState } from "react";
export const useLocalStorage = <T>(currentValue: T, appName: string): T => {
const initialValue = useRef<T>();
const isFirstRun = !initialValue.current;
useLayoutEffect(() => {
if (!initialValue.current) {
export const useLocalStorage = <T>(
defaultValue: T,
appName: string
): [T, (newValue: T) => void] => {
const [value, setValue] = useState(() => {
try {
const storageValue = localStorage.getItem(appName);
if (storageValue) {
initialValue.current = JSON.parse(storageValue);
return JSON.parse(storageValue);
} else {
initialValue.current = currentValue;
return defaultValue;
}
} catch {
initialValue.current = currentValue;
}
return defaultValue;
}
});
const setStoredValue = useCallback((newValue: T) => {
setValue(newValue);
try {
localStorage.setItem(appName, JSON.stringify(newValue));
} catch {}
}, []);
useEffect(() => {
try {
if (initialValue.current && currentValue) {
localStorage.setItem(appName, JSON.stringify(currentValue));
}
} catch {}
}, [currentValue]);
if (isFirstRun) {
return initialValue.current ?? currentValue;
} else {
return currentValue;
}
return [value, setStoredValue];
};