فهرست منبع

Improving the store API to remove need for selector and make it possible for actions to be async

Kirk Trombley 5 سال پیش
والد
کامیت
b81bb3f47e
3فایلهای تغییر یافته به همراه51 افزوده شده و 45 حذف شده
  1. 9 9
      client/src/domain/GGSHService.js
  2. 10 9
      client/src/domain/gameStore.js
  3. 32 27
      client/src/store.js

+ 9 - 9
client/src/domain/GGSHService.js

@@ -1,4 +1,4 @@
-import { selector } from "./gameStore";
+import { dispatch } from "./gameStore";
 
 const API_BASE = "https://kirkleon.ddns.net/terrassumptions/api";
 
@@ -15,7 +15,7 @@ export const getStatus = async () => {
 }
 
 export const createGame = async (timer) => {
-    const name = selector.playerName();
+    const name = dispatch.getPlayerName();
     const res = await fetch(`${API_BASE}/game`, {
         method: "PUT",
         headers: {
@@ -32,7 +32,7 @@ export const createGame = async (timer) => {
 }
 
 export const gameInfo = async () => {
-    const gameId = selector.gameId();
+    const gameId = dispatch.getGameId();
     const res = await fetch(`${API_BASE}/game/${gameId}`);
     if (!res.ok) {
         throw Error(res.statusText);
@@ -41,8 +41,8 @@ export const gameInfo = async () => {
 }
 
 export const joinGame = async () => {
-    const gameId = selector.gameId();
-    const name = selector.playerName();
+    const gameId = dispatch.getGameId();
+    const name = dispatch.getPlayerName();
     const res = await fetch(`${API_BASE}/game/${gameId}/join`, {
         method: "POST",
         headers: {
@@ -55,8 +55,8 @@ export const joinGame = async () => {
 }
 
 export const getCurrentRound = async () => {
-    const gameId = selector.gameId();
-    const name = selector.playerName();
+    const gameId = dispatch.getGameId();
+    const name = dispatch.getPlayerName();
     const res = await fetch(`${API_BASE}/game/${gameId}/current`, {
         headers: {
             "Authorization": `Name ${name}`
@@ -69,8 +69,8 @@ export const getCurrentRound = async () => {
 }
 
 export const sendGuess = async (round, point) => {
-    const gameId = selector.gameId();
-    const name = selector.playerName();
+    const gameId = dispatch.getGameId();
+    const name = dispatch.getPlayerName();
     const res = await fetch(`${API_BASE}/game/${gameId}/guesses/${round}`, {
         method: "POST",
         headers: {

+ 10 - 9
client/src/domain/gameStore.js

@@ -10,7 +10,6 @@ export const [
     useGameState,
   },
   dispatch,
-  selector,
 ] = createStore({
   gameId: null,
   playerName: null,
@@ -18,12 +17,14 @@ export const [
   gameJoined: false,
   gameState: PRE_GAME,
 }, {
-  setGameId: gameId => ({ gameId }),
-  setPlayerName: playerName => ({ playerName }),
-  joinGame: () => ({ gameJoined: true }),
-  resetGame: () => ({ gameJoined: false, gameState: PRE_GAME }),
-  startGame: () => ({ gameState: PRE_ROUND }),
-  startRound: () => ({ gameState: IN_ROUND }),
-  endRound: lastRound => ({ lastRound, gameState: POST_ROUND }),
-  endGame: () => ({ gameState: POST_GAME }),
+  setGameId: ([set], gameId) => set({ gameId }),
+  getGameId: ([_, get]) => get.gameId(),
+  setPlayerName: ([set], playerName) => set({ playerName }),
+  getPlayerName: ([_, get]) => get.playerName(),
+  joinGame: ([set]) => set({ gameJoined: true }),
+  resetGame: ([set]) => set({ gameJoined: false, gameState: PRE_GAME }),
+  startGame: ([set]) => set({ gameState: PRE_ROUND }),
+  startRound: ([set]) => set({ gameState: IN_ROUND }),
+  endRound: ([set], lastRound) => set({ lastRound, gameState: POST_ROUND }),
+  endGame: ([set]) => set({ gameState: POST_GAME }),
 });

+ 32 - 27
client/src/store.js

@@ -2,44 +2,49 @@ import { useState, useEffect } from "react";
 
 const shallowEq = (x, y) => x === y;
 
-const mapValues = (obj, fn) => Object.fromEntries(Object.entries(obj).map(([key, val]) => [key, fn(val)]))
-
 export const createStore = (initial, actions = {}) => {
-  const store = {};
+  const get = {};
+  const update = {};
   const hooks = {};
-  Object.entries(initial).forEach(([key, value]) => {
-    let _val = value;
-    let _listeners = [];
 
-    store[key] = {
-      _get: () => _val,
-      _set: newValue => {
+  const mergeState = newState => Object.entries(newState).forEach(([key, value]) => {
+    const setter = update[key];
+    if (setter) {
+      setter(value);
+    } else {
+      let _val = value;
+      let _listeners = [];
+
+      get[key] = () => _val;
+
+      update[key] = newValue => {
         const oldValue = _val;
         _val = newValue;
         _listeners
           .filter(({ equality }) => !equality(oldValue, newValue))
           .forEach(({ callback }) => callback(newValue));
-      },
-    };
-
-    // name allows linters to pick this up as a hook
-    const useValue = (equality = shallowEq) => {
-      const [val, callback] = useState(_val);
-      useEffect(() => {
-        _listeners.push({ callback, equality });
-        return () => {
-          _listeners = _listeners.filter(ln => ln.callback !== callback);
-        }
-      }, [equality]);
-      return val;
+      }
+
+      const hookName = "use" + key.charAt(0).toUpperCase() + key.slice(1);
+      // name allows linters to pick this up as a hook
+      const useValue = (equality = shallowEq) => {
+        const [val, callback] = useState(_val);
+        useEffect(() => {
+          _listeners.push({ callback, equality });
+          return () => {
+            _listeners = _listeners.filter(ln => ln.callback !== callback);
+          }
+        }, [equality]);
+        return val;
+      }
+      hooks[hookName] = useValue;
     }
-    const hookName = "use" + key.charAt(0).toUpperCase() + key.slice(1);
-    hooks[hookName] = useValue;
   });
 
-  const dispatch = mapValues(actions, act => (...args) => Object.entries(act(...args)).forEach(([key, newValue]) => store[key]?._set(newValue)));
+  mergeState(initial);
 
-  const selector = mapValues(store, obs => obs._get);
+  const dispatch = Object.fromEntries(Object.entries(actions)
+    .map(([key, act]) => [key, (...args) => act([mergeState, get], ...args)]));
 
-  return [hooks, dispatch, selector];
+  return [hooks, dispatch];
 }