useXxxReducer

This commit is contained in:
djmil 2023-11-08 09:23:20 +01:00
parent 9ec2059c4a
commit b58c71c876
9 changed files with 84 additions and 56 deletions

View File

@ -1,31 +1,34 @@
import './App.css'; import './App.css';
import React, { useReducer } from 'react' import React from 'react';
import { BrowserRouter, Routes, Route } from "react-router-dom" import { BrowserRouter, Routes, Route } from "react-router-dom";
import Header from "./container/Header" import Header from "./container/Header"
import Leaderboard from "./container/Leaderboard" import Leaderboard from "./container/Leaderboard"
import Game from "./components/Game" import Game from "./components/Game"
import About from "./components/About" import About from "./components/About"
import Polling from './flux/polling'; import usePollingReducer from './reducer/polling';
import User from './flux/user'; import useUserReducer from './reducer/user';
import useLeaderboardReducer from './reducer/leaderboard';
import useLeaderboardApi from './api/leaderboard'; import useLeaderboardApi from './api/leaderboard';
import useUserApi from './api/user'; import useUserApi from './api/user';
function App() { function App() {
const pollingFlux = useReducer(Polling.reducer, Polling.initialState); const pollingReducer = usePollingReducer();
const userFlux = useReducer(User.reducer, User.initialState); const userReducer = useUserReducer();
const leaderboardReducer = useLeaderboardReducer();
const leaderboardApi = useLeaderboardApi(pollingFlux); const leaderboardApi = useLeaderboardApi(leaderboardReducer);
const userApi = useUserApi(userFlux); const userApi = useUserApi(userReducer);
const leaderboard = leaderboardApi.get(); const leaderboard = leaderboardApi.get(pollingReducer);
const user = userApi.get(); const user = userApi.get();
return ( return (
<div className="App" > <div className="App" >
<BrowserRouter> <BrowserRouter>
<Header pollingFlux={pollingFlux} /> <Header pollingReducer={pollingReducer} />
<Routes> <Routes>
{/* https://stackoverflow.com/questions/40541994/multiple-path-names-for-a-same-component-in-react-router */} {/* https://stackoverflow.com/questions/40541994/multiple-path-names-for-a-same-component-in-react-router */}
<Route path="/game" element={<Game />} /> <Route path="/game" element={<Game />} />

View File

@ -2,19 +2,26 @@ import usePolling from "../util/Polling"
const uri = '/api/leaderboard'; const uri = '/api/leaderboard';
export default function useLeaderboardApi([polling, dispatchPolling]) { export default function useLeaderboardApi(leaderboardReducer) {
const [leaderboard, dispatchLeaderboaed] = leaderboardReducer;
const useGet = (pollingReducer) => {
const [polling, dispatchPolling] = pollingReducer;
const useGet = () => {
const mode = (polling.enabled === true) const mode = (polling.enabled === true)
? { interval_sec: 300 } // update leaderbord stats every 5 min ? { interval_sec: 300 } // update leaderbord stats every 5 min
: { interval_stop: true } // user has fliped OfflineToggel : { interval_stop: true } // user has fliped OfflineToggel
const [leaderboard, isFetching] = usePolling(uri, mode); const [table, isFetching] = usePolling(uri, mode);
if (polling.leaderboard !== isFetching) { if (polling.leaderboard !== isFetching) {
dispatchPolling({ type: 'next', leaderboard: isFetching }); dispatchPolling({ type: 'next', leaderboard: isFetching });
} }
if (leaderboard.table !== table) {
dispatchLeaderboaed({ type: 'next', table });
}
return leaderboard; return leaderboard;
} }

View File

@ -4,8 +4,8 @@ import { NavLink } from "react-router-dom";
import OnlineToggle from '../components/OnlineToggle'; import OnlineToggle from '../components/OnlineToggle';
import Wobler from '../components/Wobler'; import Wobler from '../components/Wobler';
export default function Header({ pollingFlux }) { export default function Header({ pollingReducer }) {
const [polling, dispatchPolling] = pollingFlux; const [polling, dispatchPolling] = pollingReducer;
return ( return (
<div className='Header'> <div className='Header'>
@ -33,4 +33,4 @@ export default function Header({ pollingFlux }) {
</nav> </nav>
</div> </div>
) )
} }

View File

@ -4,14 +4,15 @@ import Loading from '../components/Loading';
export default function Leaderboard({ leaderboard, user }) { export default function Leaderboard({ leaderboard, user }) {
if (leaderboard == null) const table = leaderboard?.table;
if (!table)
return <Loading /> return <Loading />
const isCurrentUser = (playerName) => const isCurrentUser = (playerName) =>
user.isCurrentUser(playerName) === true ? true : null; user?.isCurrentUser(playerName) === true ? true : null;
const tableRows = Object.keys(leaderboard).map(playerName => { const tableRows = Object.keys(table).map(playerName => {
var rank = leaderboard[playerName]; var rank = table[playerName];
return <tr key={playerName} className={isCurrentUser(playerName) && 'currentuser'}> return <tr key={playerName} className={isCurrentUser(playerName) && 'currentuser'}>
<td>{playerName}</td> <td>{playerName}</td>

View File

@ -1,28 +0,0 @@
import { localeCompare } from '../util/Locale'
import StateHelper from '../util/StateHelper';
export const userInitialState = {
username: '',
isCurrentUser: function (otherUsername) {
return localeCompare(this.username, otherUsername)
},
};
export function userReducer(state, action) {
switch (action.type) {
case 'next':
return StateHelper.next(state, action);
default:
throw Error('Unknown action.type: ' + action.type);
}
}
const User = {
reducer: userReducer,
initialState: userInitialState
};
export default User;

View File

@ -0,0 +1,21 @@
import { useReducer } from 'react';
import { nextState } from '../util/StateHelper';
export const leaderboardInitialState = {
table: null,
};
export function leaderboardReducer(state, action) {
switch (action.type) {
case 'next':
return nextState(state, action);
default:
throw Error('LeaderboardReducer: Unknown action.type', action.type);
}
}
export default function useLeaderboardReducer() {
return useReducer(leaderboardReducer, leaderboardInitialState);
}

View File

@ -1,4 +1,5 @@
import { useLocalStorage } from '../util/PersistentStorage' import { useReducer } from 'react';
import { useLocalStorage } from '../util/PersistentStorage';
import { nextState } from '../util/StateHelper'; import { nextState } from '../util/StateHelper';
const Persistent = (() => { const Persistent = (() => {
@ -33,9 +34,6 @@ export function pollingReducer(curntState, action) {
} }
} }
const Polling = { export default function usePollingReducer() {
reducer: pollingReducer, return useReducer(pollingReducer, pollingInitialState);
initialState: pollingInitialState }
};
export default Polling

View File

@ -0,0 +1,26 @@
import { useReducer } from 'react';
import { localeCompare } from '../util/Locale';
import { nextState } from '../util/StateHelper';
export const userInitialState = {
username: '',
isCurrentUser: function (otherUsername) {
return localeCompare(this.username, otherUsername)
}
};
export function userReducer(state, action) {
switch (action.type) {
case 'next':
return nextState(state, action);
default:
throw Error('Unknown action.type', action.type);
}
}
export default function useUserReducer() {
return useReducer(userReducer, userInitialState);
}

View File

@ -47,7 +47,7 @@ export default function usePolling(url, mode) {
}, [url, mode, isFetching, cache, fetchData, delayID]); }, [url, mode, isFetching, cache, fetchData, delayID]);
return [ return [
cache, // API response cache, // API responce
isFetching // true / false isFetching // true / false
] ]
} }