react: state -> reducer -> context
- Leaderboard: useState - User: useReducer - Games: useContext [...in progress] - usePolling giveup on internal cache in favour of onResponce() callback
This commit is contained in:
parent
7314b8c328
commit
3f47654cf2
@ -9,40 +9,36 @@ import About from "./components/About"
|
|||||||
import Games from './container/Games';
|
import Games from './container/Games';
|
||||||
import Leaderboard from './container/Leaderboard';
|
import Leaderboard from './container/Leaderboard';
|
||||||
|
|
||||||
import useUserReducer from './reducer/user';
|
|
||||||
import usePollingReducer from './reducer/polling';
|
import usePollingReducer from './reducer/polling';
|
||||||
import useLeaderboardReducer from './reducer/leaderboard';
|
//import useGamesReducer from './reducer/games';
|
||||||
import useGamesReducer from './reducer/games';
|
|
||||||
|
|
||||||
import useUserApi from './api/user';
|
import useUserApi from './api/user';
|
||||||
import useLeaderboardApi from './api/leaderboard';
|
import useLeaderboardApi from './api/leaderboard';
|
||||||
import useGamesApi from './api/games';
|
//import useGamesApi from './api/games';
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const userReducer = useUserReducer();
|
const pollingReducer = usePollingReducer();
|
||||||
const pollingFlux = usePollingReducer();
|
//const gamesReducer = useGamesReducer();
|
||||||
const leaderboardReducer = useLeaderboardReducer();
|
|
||||||
const gamesReducer = useGamesReducer();
|
|
||||||
|
|
||||||
const user = useUserApi(userReducer).get();
|
//const games = useGamesApi(gamesReducer).list(pollingReducer);
|
||||||
const leaderboard = useLeaderboardApi(leaderboardReducer).poll(pollingFlux);
|
const leaderboard = useLeaderboardApi().poll(pollingReducer);
|
||||||
/*const gamesApi = */ useGamesApi(gamesReducer).list(pollingFlux);
|
const user = useUserApi().get();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Header pollingFlux={pollingFlux} />
|
<Header pollingReducer={pollingReducer} />
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<About />} />
|
<Route path='/' element={<About />} />
|
||||||
<Route path="/about" element={<About />} />
|
<Route path='/about' element={<About />} />
|
||||||
<Route path="/games/*" element={<Games />} />
|
<Route path='/games/*' element={<Games />} />
|
||||||
<Route path="/leaderboard" element={<Leaderboard leaderboard={leaderboard} user={user} />} />
|
<Route path='/leaderboard' element={<Leaderboard leaderboard={leaderboard} user={user} />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Header({ pollingFlux }) {
|
function Header({ pollingReducer }) {
|
||||||
const [polling, dispatchPolling] = pollingFlux;
|
const [polling, dispatchPolling] = pollingReducer;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='Header'>
|
<div className='Header'>
|
||||||
@ -52,20 +48,20 @@ function Header({ pollingFlux }) {
|
|||||||
|
|
||||||
<OnlineToggle
|
<OnlineToggle
|
||||||
isOnline={polling.enabled}
|
isOnline={polling.enabled}
|
||||||
onClick={() => dispatchPolling({ type: "toggleOnOff" })}
|
onClick={() => dispatchPolling({ type: 'toggleOnOff' })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<NavLink to="/about">
|
<NavLink to='/about'>
|
||||||
About
|
About
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
<NavLink to="/games">
|
<NavLink to='/games'>
|
||||||
<Wobler text="Games" dance={polling.games} />
|
<Wobler text="Games" dance={polling.games} />
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
<NavLink to="/leaderboard">
|
<NavLink to='/leaderboard'>
|
||||||
<Wobler text="Leaderboard" dance={polling.leaderboard} />
|
<Wobler text='Leaderboard' dance={polling.leaderboard} />
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,25 +1,19 @@
|
|||||||
import usePolling from "../util/Polling"
|
import usePolling from "../util/Polling"
|
||||||
|
|
||||||
const uri = '/api/games';
|
export default function useGamesApi(gamesState) {
|
||||||
|
const [games, setGames] = gamesState;
|
||||||
export default function useGamesApi(gamesReducer) {
|
|
||||||
const [games, dispatchGames] = gamesReducer;
|
|
||||||
|
|
||||||
const useList = (pollingReducer) => {
|
const useList = (pollingReducer) => {
|
||||||
const [polling, dispatchPolling] = pollingReducer;
|
const [polling, dispatchPolling] = pollingReducer;
|
||||||
|
|
||||||
const mode = (polling.enabled === true)
|
const mode = (polling.enabled === true)
|
||||||
? { interval_sec: 30 } // update games list half a minue
|
? { interval_sec: 30 } // update games list every half a minue
|
||||||
: { interval_stop: true } // user has fliped OfflineToggel
|
: { interval_stop: true } // user has fliped OfflineToggel
|
||||||
|
|
||||||
const [list, isFetching] = usePolling(uri, mode);
|
const isPolling = usePolling('/api/games', setGames, mode);
|
||||||
|
|
||||||
if (polling.games !== isFetching) {
|
if (isPolling !== polling.games) {
|
||||||
dispatchPolling({ type: 'next', games: isFetching });
|
dispatchPolling({ type: 'next', games: isPolling });
|
||||||
}
|
|
||||||
|
|
||||||
if (games.list !== list) {
|
|
||||||
dispatchGames({ type: 'next', list });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return games;
|
return games;
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import usePolling from "../util/Polling"
|
import { useState } from "react";
|
||||||
|
import usePolling from "../util/Polling";
|
||||||
|
|
||||||
const uri = '/api/leaderboard';
|
export default function useLeaderboardApi() {
|
||||||
|
const [leaderboard, setLeaderboard] = useState(null);
|
||||||
export default function useLeaderboardApi(leaderboardReducer) {
|
|
||||||
const [leaderboard, dispatchLeaderboaed] = leaderboardReducer;
|
|
||||||
|
|
||||||
const usePoll = (pollingReducer) => {
|
const usePoll = (pollingReducer) => {
|
||||||
const [polling, dispatchPolling] = pollingReducer;
|
const [polling, dispatchPolling] = pollingReducer;
|
||||||
@ -12,14 +11,10 @@ export default function useLeaderboardApi(leaderboardReducer) {
|
|||||||
? { 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 [table, isFetching] = usePolling(uri, mode);
|
const isPolling = usePolling('/api/leaderboard', setLeaderboard, mode);
|
||||||
|
|
||||||
if (polling.leaderboard !== isFetching) {
|
if (isPolling !== polling.leaderboard) {
|
||||||
dispatchPolling({ type: 'next', leaderboard: isFetching });
|
dispatchPolling({ type: 'next', leaderboard: isPolling });
|
||||||
}
|
|
||||||
|
|
||||||
if (leaderboard.table !== table) {
|
|
||||||
dispatchLeaderboaed({ type: 'next', table });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return leaderboard;
|
return leaderboard;
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
import usePolling from "../util/Polling"
|
import usePolling from "../util/Polling";
|
||||||
|
import useUserReducer from "../reducer/user";
|
||||||
|
|
||||||
const uri = '/api/user';
|
export default function useUserApi() {
|
||||||
|
const [user, dispatchUser] = useUserReducer();
|
||||||
export default function useUserApi([user, dispatchUser]) {
|
|
||||||
|
|
||||||
const useGet = () => {
|
const useGet = () => {
|
||||||
const [nextUser] = usePolling(uri);
|
const onResponce = (json) => {
|
||||||
|
dispatchUser({ type: "parse", json });
|
||||||
if (typeof nextUser?.username === 'string' && nextUser.username !== user.username) {
|
|
||||||
dispatchUser({ type: "next", username: nextUser.username });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usePolling('/api/user', onResponce); // <<-- fetch once
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,15 +4,14 @@ import Loading from '../components/Loading';
|
|||||||
|
|
||||||
export default function Leaderboard({ leaderboard, user }) {
|
export default function Leaderboard({ leaderboard, user }) {
|
||||||
|
|
||||||
const table = leaderboard?.table;
|
if (leaderboard == null)
|
||||||
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(table).map(playerName => {
|
const tableRows = Object.keys(leaderboard).map(playerName => {
|
||||||
var rank = table[playerName];
|
var rank = leaderboard[playerName];
|
||||||
|
|
||||||
return <tr key={playerName} className={isCurrentUser(playerName) && 'currentuser'}>
|
return <tr key={playerName} className={isCurrentUser(playerName) && 'currentuser'}>
|
||||||
<td>{playerName}</td>
|
<td>{playerName}</td>
|
||||||
|
@ -1,84 +0,0 @@
|
|||||||
export const reducer = (state, action) => {
|
|
||||||
|
|
||||||
switch (action.update) {
|
|
||||||
|
|
||||||
case "game-selector":
|
|
||||||
return GameSelector_update(state, action)
|
|
||||||
|
|
||||||
case "newGame":
|
|
||||||
return updateNewGame(state, action)
|
|
||||||
|
|
||||||
default:
|
|
||||||
console.warn("Unknown action.component", action.component)
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const initialState = {
|
|
||||||
gameSelector: {
|
|
||||||
selectedGameProposal: null,
|
|
||||||
selectedActiveGame: null,
|
|
||||||
selectedArchiveGame: null,
|
|
||||||
},
|
|
||||||
|
|
||||||
newGame: {
|
|
||||||
whitePlayer: '',
|
|
||||||
blackPlayer: '',
|
|
||||||
message: '',
|
|
||||||
fetching: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function GameSelector_update(state, action) {
|
|
||||||
if (Object.hasOwn(action, 'selectedGameProposal')) {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
gameSelector: {
|
|
||||||
...state.gameSelector,
|
|
||||||
selectedGameProposal: action.selectedGameProposal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.hasOwn(action, 'selectedActiveGame')) {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
gameSelector: {
|
|
||||||
...state.gameSelector,
|
|
||||||
selectedActiveGame: action.selectedActiveGame
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.hasOwn(action, 'selectedArchiveGame')) {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
gameSelector: {
|
|
||||||
...state.gameSelector,
|
|
||||||
selectedArchiveGame: action.selectedArchiveGame
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.warn(action.component, "- bad property")
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateNewGame(state, action) {
|
|
||||||
const newGame = {...state.newGame}
|
|
||||||
|
|
||||||
Object.keys(action)
|
|
||||||
.slice(1) // skip 'update' property
|
|
||||||
.forEach(actionKey => {
|
|
||||||
if (Object.hasOwn(newGame, actionKey)) {
|
|
||||||
newGame[actionKey] = action[actionKey]
|
|
||||||
} else {
|
|
||||||
console.warn("NewGame update: bad action property\n", actionKey + ":", action[actionKey])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
newGame
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
import { useState, useCallback, useEffect, } from "react"
|
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: Poll(uri, flavour)
|
|
||||||
- uri: string
|
|
||||||
- execution_flvour:
|
|
||||||
- once (i.e. now)
|
|
||||||
- interval (sec)
|
|
||||||
- stop
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default function Poll(url, interval_sec, offlineMode) {
|
|
||||||
const [dataCache, setDataCache] = useState(null)
|
|
||||||
const [fetching , setFetching ] = useState(false)
|
|
||||||
const [timeoutID, setTimeoutID] = useState(null)
|
|
||||||
|
|
||||||
const fecthData = useCallback(() => {
|
|
||||||
setTimeoutID(null)
|
|
||||||
setFetching(true)
|
|
||||||
|
|
||||||
fetch(url)
|
|
||||||
.then((response) => {
|
|
||||||
setFetching(false)
|
|
||||||
return response.json()
|
|
||||||
})
|
|
||||||
.then((freshData) => setDataCache(freshData))
|
|
||||||
.catch((err) => console.log(err.message))
|
|
||||||
}, [url])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (dataCache == null) {
|
|
||||||
fecthData() // <<-- run immediatly on startup
|
|
||||||
}
|
|
||||||
else if (offlineMode === true) {
|
|
||||||
clearTimeout(timeoutID) // cancel already scheduled fetch
|
|
||||||
setTimeoutID(null) // & stop interval fetching
|
|
||||||
}
|
|
||||||
else if (timeoutID === null && typeof interval_sec === 'number') {
|
|
||||||
const timeoutID = setTimeout(fecthData, interval_sec * 1000)
|
|
||||||
setTimeoutID(timeoutID)
|
|
||||||
console.log("Fetch '" +url +"' scheduled in " +interval_sec +" sec")
|
|
||||||
}
|
|
||||||
}, [url, dataCache, fecthData, timeoutID, offlineMode, interval_sec]);
|
|
||||||
|
|
||||||
return [ dataCache, fetching ]
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
import React from "react"
|
|
||||||
import { reducer, initialState } from "./reducer"
|
|
||||||
|
|
||||||
import Poll from "./Poll"
|
|
||||||
|
|
||||||
export const AppData = React.createContext({
|
|
||||||
state: initialState,
|
|
||||||
dispatch: () => null
|
|
||||||
})
|
|
||||||
|
|
||||||
export const AppDataProvider = ({ children }) => {
|
|
||||||
|
|
||||||
const [data, dispatchData] = React.useReducer(reducer, initialState)
|
|
||||||
|
|
||||||
const [games, gamesFetching ] = Poll('/api/gamestate' , 30, data.offlineMode)
|
|
||||||
const [leaderboard, leaderboardFetching ] = Poll('/api/leaderboard', 60, data.offlineMode)
|
|
||||||
const [user] = Poll('/api/user') // once
|
|
||||||
|
|
||||||
data.games = games
|
|
||||||
data.gamesFetching = gamesFetching
|
|
||||||
|
|
||||||
data.leaderboard = leaderboard
|
|
||||||
data.leaderboardFetching = leaderboardFetching
|
|
||||||
|
|
||||||
data.isCurrentUser = (otherUsername) => {
|
|
||||||
return user?.username && ciEquals(user.username, otherUsername) ? true : null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AppData.Provider value={[data, dispatchData]}>
|
|
||||||
{children}
|
|
||||||
</AppData.Provider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function ciEquals(a, b) {
|
|
||||||
return typeof a === 'string' && typeof b === 'string'
|
|
||||||
? a.localeCompare(b, undefined, { sensitivity: 'accent' }) === 0
|
|
||||||
: a === b;
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
export const reducer = (state, action) => {
|
|
||||||
switch (action.type) {
|
|
||||||
|
|
||||||
case "toggleOfflineMode":
|
|
||||||
return { ...state,
|
|
||||||
offlineMode: !state.offlineMode // on/off
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
console.warn("Unknown action.type", action)
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const initialState = {
|
|
||||||
games: null,
|
|
||||||
gamesFetching: false,
|
|
||||||
|
|
||||||
leaderboard: null,
|
|
||||||
leaderboardFetching: false,
|
|
||||||
|
|
||||||
isCurrentUser: () => null,
|
|
||||||
|
|
||||||
offlineMode: false
|
|
||||||
}
|
|
@ -1,17 +1,17 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import { reducer, initialState } from "./reducer"
|
import { reducer, initialState } from "./reducer"
|
||||||
|
|
||||||
export const AppContext = React.createContext({
|
export const Games = React.createContext({
|
||||||
state: initialState,
|
state: initialState,
|
||||||
dispatch: () => null
|
dispatch: () => null
|
||||||
})
|
})
|
||||||
|
|
||||||
export const AppContextProvider = ({ children }) => {
|
export const GamesProvider = ({ children }) => {
|
||||||
const [state, dispatch] = React.useReducer(reducer, initialState)
|
const [state, dispatch] = React.useReducer(reducer, initialState)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppContext.Provider value={[ state, dispatch ]}>
|
<Games.Provider value={[ state, dispatch ]}>
|
||||||
{ children }
|
{ children }
|
||||||
</AppContext.Provider>
|
</Games.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -3,18 +3,11 @@ import ReactDOM from 'react-dom/client';
|
|||||||
import './index.css';
|
import './index.css';
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from './reportWebVitals';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import { AppDataProvider } from "./context/data"
|
|
||||||
import { AppContextProvider } from "./context/app"
|
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||||
root.render(
|
root.render(
|
||||||
|
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<AppContextProvider>
|
<App />
|
||||||
<AppDataProvider>
|
|
||||||
<App />
|
|
||||||
</AppDataProvider>
|
|
||||||
</AppContextProvider>
|
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
import { useReducer } from 'react';
|
import { useReducer } from 'react';
|
||||||
import { localeCompare } from '../util/Locale';
|
import { localeCompare } from '../util/Locale';
|
||||||
import { nextState } from '../util/StateHelper';
|
//import { nextState } from '../util/StateHelper';
|
||||||
|
|
||||||
export const userInitialState = {
|
export const userInitialState = {
|
||||||
username: '',
|
username: '',
|
||||||
@ -13,8 +13,13 @@ export const userInitialState = {
|
|||||||
export function userReducer(state, action) {
|
export function userReducer(state, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
|
||||||
case 'next':
|
case 'parse':
|
||||||
return nextState(state, action);
|
const apiData = parse(action.json);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...apiData
|
||||||
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw Error('UserReducer: unknown action.type', action.type);
|
throw Error('UserReducer: unknown action.type', action.type);
|
||||||
@ -24,3 +29,10 @@ export function userReducer(state, action) {
|
|||||||
export default function useUserReducer() {
|
export default function useUserReducer() {
|
||||||
return useReducer(userReducer, userInitialState);
|
return useReducer(userReducer, userInitialState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parse(json) {
|
||||||
|
console.log("userreducer.parse", json);
|
||||||
|
return {
|
||||||
|
username: json.username
|
||||||
|
}
|
||||||
|
}
|
@ -3,51 +3,53 @@ import { useState, useCallback, useEffect, } from "react"
|
|||||||
/*
|
/*
|
||||||
- uri: string
|
- uri: string
|
||||||
- mode:
|
- mode:
|
||||||
- null - default, return cashed value while polling fresh one from server)
|
- null - default, fetch data ONCE
|
||||||
- interval_sec
|
- interval_sec
|
||||||
- interval_stop
|
- interval_stop
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default function usePolling(url, mode) {
|
export default function usePolling(uri, onResponce, mode = null) {
|
||||||
const [cache, setCache] = useState(null);
|
const [initialPoll, setInitialPoll] = useState(true);
|
||||||
const [isFetching, setFetching] = useState(false);
|
const [isPolling, setPolling] = useState(false);
|
||||||
const [delayID, setDelayID] = useState(null);
|
const [intervalTimer, setIntervalTimer] = useState(null);
|
||||||
|
|
||||||
const fetchData = useCallback(() => {
|
const pollData = useCallback(() => {
|
||||||
setDelayID(null);
|
setPolling(true);
|
||||||
setFetching(true);
|
setInitialPoll(false);
|
||||||
|
|
||||||
fetch(url)
|
fetch(uri)
|
||||||
.then((response) => response.json())
|
.then((responce) => {
|
||||||
.then((freshData) => {
|
setPolling(false);
|
||||||
setCache(freshData);
|
|
||||||
setFetching(false);
|
if (typeof mode?.interval_sec === 'number') {
|
||||||
|
console.log("Schedule", uri, "fetch in", mode.interval_sec, "sec");
|
||||||
|
const intervalTimer = setTimeout(pollData, mode.interval_sec * 1000);
|
||||||
|
setIntervalTimer(intervalTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return responce.json();
|
||||||
|
})
|
||||||
|
.then((json) => {
|
||||||
|
onResponce(json);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.warn(err.message);
|
console.warn(err.message);
|
||||||
setFetching(false);
|
|
||||||
})
|
})
|
||||||
}, [url])
|
}, [uri, mode, onResponce]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (cache === null && isFetching === false) {
|
if ((initialPoll || (typeof mode?.interval_sec === 'number' && intervalTimer === null)) && !isPolling) {
|
||||||
fetchData();
|
pollData();
|
||||||
}
|
}
|
||||||
|
}, [initialPoll, mode, intervalTimer, isPolling, pollData]);
|
||||||
|
|
||||||
if (mode?.interval_sec && delayID === null) {
|
if (mode?.interval_stop && intervalTimer) {
|
||||||
const timeoutID = setTimeout(fetchData, mode.interval_sec * 1000)
|
console.log("Cancel scheduled fetch for", uri);
|
||||||
setDelayID(timeoutID)
|
clearTimeout(intervalTimer);
|
||||||
console.log("Fetch '" + url + "' scheduled in " + mode.interval_sec + " sec")
|
setIntervalTimer(null);
|
||||||
}
|
setInitialPoll(true);
|
||||||
else if (mode?.interval_stop) {
|
}
|
||||||
clearTimeout(delayID); // cancel already scheduled fetch
|
|
||||||
setDelayID(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [url, mode, isFetching, cache, fetchData, delayID]);
|
|
||||||
|
|
||||||
return [
|
return isPolling;
|
||||||
cache, // API responce
|
|
||||||
isFetching // true / false
|
|
||||||
]
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user