refactor: Convert App class component to functional component

This commit is contained in:
2025-07-07 17:22:56 +00:00
committed by Tanner Collin
parent 5cdbf6ef54
commit 633429c976

View File

@@ -1,4 +1,4 @@
import React from 'react'; import React, { useState, useEffect, useRef, useCallback } from 'react';
import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom'; import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom';
import localForage from 'localforage'; import localForage from 'localforage';
import './Style-light.css'; import './Style-light.css';
@@ -15,70 +15,63 @@ import Submit from './Submit.js';
import Results from './Results.js'; import Results from './Results.js';
import ScrollToTop from './ScrollToTop.js'; import ScrollToTop from './ScrollToTop.js';
class App extends React.Component { function App() {
constructor(props) { const [theme, setTheme] = useState(localStorage.getItem('theme') || '');
super(props); const cache = useRef({});
const [isFullScreen, setIsFullScreen] = useState(!!document.fullscreenElement);
this.state = { const updateCache = useCallback((key, value) => {
theme: localStorage.getItem('theme') || '', cache.current[key] = value;
}; }, []);
this.cache = {}; const light = () => {
} setTheme('');
updateCache = (key, value) => {
this.cache[key] = value;
}
light() {
this.setState({ theme: '' });
localStorage.setItem('theme', ''); localStorage.setItem('theme', '');
} };
dark() { const dark = () => {
this.setState({ theme: 'dark' }); setTheme('dark');
localStorage.setItem('theme', 'dark'); localStorage.setItem('theme', 'dark');
} };
black() { const black = () => {
this.setState({ theme: 'black' }); setTheme('black');
localStorage.setItem('theme', 'black'); localStorage.setItem('theme', 'black');
} };
red() { const red = () => {
this.setState({ theme: 'red' }); setTheme('red');
localStorage.setItem('theme', 'red'); localStorage.setItem('theme', 'red');
} };
componentDidMount() { useEffect(() => {
if (!this.cache.length) { if (Object.keys(cache.current).length === 0) {
localForage.iterate((value, key) => { localForage.iterate((value, key) => {
this.updateCache(key, value); updateCache(key, value);
}).then(() => {
console.log('loaded cache from localforage');
}); });
console.log('loaded cache from localforage');
} }
} }, [updateCache]);
goFullScreen() { const goFullScreen = () => {
if ('wakeLock' in navigator) { if ('wakeLock' in navigator) {
navigator.wakeLock.request('screen'); navigator.wakeLock.request('screen');
} }
document.body.requestFullscreen({ navigationUI: 'hide' });
document.body.requestFullscreen({ navigationUI: 'hide' }).then(() => {
window.addEventListener('resize', () => this.forceUpdate());
this.forceUpdate();
});
}; };
exitFullScreen() { const exitFullScreen = () => {
document.exitFullscreen().then(() => { document.exitFullscreen();
this.forceUpdate();
});
}; };
render() { useEffect(() => {
const theme = this.state.theme; const onFullScreenChange = () => setIsFullScreen(!!document.fullscreenElement);
document.addEventListener('fullscreenchange', onFullScreenChange);
return () => document.removeEventListener('fullscreenchange', onFullScreenChange);
}, []);
useEffect(() => {
if (theme === 'dark') { if (theme === 'dark') {
document.body.style.backgroundColor = '#1a1a1a'; document.body.style.backgroundColor = '#1a1a1a';
} else if (theme === 'black') { } else if (theme === 'black') {
@@ -88,49 +81,49 @@ class App extends React.Component {
} else { } else {
document.body.style.backgroundColor = '#eeeeee'; document.body.style.backgroundColor = '#eeeeee';
} }
}, [theme]);
const fullScreenAvailable = document.fullscreenEnabled || const fullScreenAvailable = document.fullscreenEnabled ||
document.mozFullscreenEnabled || document.mozFullscreenEnabled ||
document.webkitFullscreenEnabled || document.webkitFullscreenEnabled ||
document.msFullscreenEnabled; document.msFullscreenEnabled;
return ( return (
<div className={theme}> <div className={theme}>
<Router> <Router>
<div className='container menu'> <div className='container menu'>
<p> <p>
<Link to='/'>QotNews</Link> <Link to='/'>QotNews</Link>
<span className='theme'><a href='#' onClick={() => this.light()}>Light</a> - <a href='#' onClick={() => this.dark()}>Dark</a> - <a href='#' onClick={() => this.black()}>Black</a> - <a href='#' onClick={() => this.red()}>Red</a></span> <span className='theme'><a href='#' onClick={() => light()}>Light</a> - <a href='#' onClick={() => dark()}>Dark</a> - <a href='#' onClick={() => black()}>Black</a> - <a href='#' onClick={() => red()}>Red</a></span>
<br /> <br />
<span className='slogan'>Hacker News, Reddit, Lobsters, and Tildes articles rendered in reader mode.</span> <span className='slogan'>Hacker News, Reddit, Lobsters, and Tildes articles rendered in reader mode.</span>
</p> </p>
<Route path='/(|search)' component={Search} /> <Route path='/(|search)' component={Search} />
<Route path='/(|search)' component={Submit} /> <Route path='/(|search)' component={Submit} />
{fullScreenAvailable && {fullScreenAvailable &&
<Route path='/(|search)' render={() => !document.fullscreenElement ? <Route path='/(|search)' render={() => !isFullScreen ?
<button className='fullscreen' onClick={() => this.goFullScreen()}>Enter Fullscreen</button> <button className='fullscreen' onClick={() => goFullScreen()}>Enter Fullscreen</button>
: :
<button className='fullscreen' onClick={() => this.exitFullScreen()}>Exit Fullscreen</button> <button className='fullscreen' onClick={() => exitFullScreen()}>Exit Fullscreen</button>
} /> } />
} }
</div> </div>
<Route path='/' exact render={(props) => <Feed {...props} updateCache={this.updateCache} />} /> <Route path='/' exact render={(props) => <Feed {...props} updateCache={updateCache} />} />
<Switch> <Switch>
<Route path='/search' component={Results} /> <Route path='/search' component={Results} />
<Route path='/:id' exact render={(props) => <Article {...props} cache={this.cache} />} /> <Route path='/:id' exact render={(props) => <Article {...props} cache={cache.current} />} />
</Switch> </Switch>
<Route path='/:id/c' exact render={(props) => <Comments {...props} cache={this.cache} />} /> <Route path='/:id/c' exact render={(props) => <Comments {...props} cache={cache.current} />} />
<BackwardDot /> <BackwardDot />
<ForwardDot /> <ForwardDot />
<ScrollToTop /> <ScrollToTop />
</Router> </Router>
</div> </div>
); );
}
} }
export default App; export default App;