From a690478ef3de4e06deb68d7b5cedbc9f2b6eb6e3 Mon Sep 17 00:00:00 2001 From: martin-mfg <2026226+martin-mfg@users.noreply.github.com> Date: Sun, 23 Nov 2025 14:24:36 +0100 Subject: [PATCH] refactor useSelector usage, use default userId everywhere --- frontend/frontend/src/axios-override.js | 9 ++++--- frontend/frontend/src/components/Card/SVG.js | 21 ++++++++-------- frontend/frontend/src/pages/App/AppTrends.js | 18 +++++++------- frontend/frontend/src/pages/App/Header.js | 6 ++--- frontend/frontend/src/pages/Auth/SignUp.js | 4 ---- frontend/frontend/src/pages/Home/Home.js | 14 +++++++---- .../frontend/src/pages/Home/stages/Login.js | 14 +++++++---- .../src/pages/Home/stages/SelectCard.js | 8 +++---- .../frontend/src/pages/Landing/Landing.js | 6 ++--- .../frontend/src/pages/Settings/Settings.js | 16 +++++++++---- .../src/redux/selectors/userSelectors.js | 24 +++++++++++++++++++ 11 files changed, 85 insertions(+), 55 deletions(-) create mode 100644 frontend/frontend/src/redux/selectors/userSelectors.js diff --git a/frontend/frontend/src/axios-override.js b/frontend/frontend/src/axios-override.js index a69372da..da3ba872 100644 --- a/frontend/frontend/src/axios-override.js +++ b/frontend/frontend/src/axios-override.js @@ -35,18 +35,17 @@ function createMockResponse(data, config) { }); } -// store userId outside React context so the interceptor can access it -let userId = null; +// store isAuthenticated outside React context so the interceptor can access it +let isAuthenticated = null; -export function setUserId(newUserId) { - userId = newUserId; +export function setIsAuthenticated(newIsAuthenticated) { + isAuthenticated = newIsAuthenticated; } const defaultAdapter = getAdapter(axios.defaults.adapter); // mock responses to unauthenticated "anuraghazra" requests axios.defaults.adapter = async (config) => { - const isAuthenticated = userId && userId.length > 0; if (isAuthenticated) { return defaultAdapter(config); } diff --git a/frontend/frontend/src/components/Card/SVG.js b/frontend/frontend/src/components/Card/SVG.js index 9e59251e..fb486538 100644 --- a/frontend/frontend/src/components/Card/SVG.js +++ b/frontend/frontend/src/components/Card/SVG.js @@ -4,27 +4,29 @@ import React, { useEffect, useRef, useState } from 'react'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; - import Skeleton from 'react-loading-skeleton'; import 'react-loading-skeleton/dist/skeleton.css'; import { createMockReq, createMockRes } from '../../mock-http'; import { default as router } from '../../backend/.vercel/output/functions/api.func/router.js'; -import { setUserId } from '../../axios-override'; +import { setIsAuthenticated } from '../../axios-override'; +import { + useIsAuthenticated, + useUserToken, +} from '../../redux/selectors/userSelectors'; const SvgInline = (props) => { const [svg, setSvg] = useState(null); const [loaded, setLoaded] = useState(false); const containerRef = useRef(null); - const userToken = useSelector((state) => state.user.token); - const userId = useSelector((state) => state.user.userId); + const userToken = useUserToken(); + const isAuthenticated = useIsAuthenticated(); const { url } = props; - // provide userId to non-react code in axios-override.js + // provide isAuthenticated to non-react code in axios-override.js useEffect(() => { - setUserId(userId); - }, [userId]); + setIsAuthenticated(isAuthenticated); + }, [isAuthenticated]); useEffect(() => { const loadSvg = async () => { @@ -35,7 +37,6 @@ const SvgInline = (props) => { let body; let status; - const isAuthenticated = userId && userId.length > 0; if (isAuthenticated && (!userToken || userToken === 'placeholderPAT')) { // waiting for backend call to private-access return; @@ -67,7 +68,7 @@ const SvgInline = (props) => { setLoaded(true); }; loadSvg(); - }, [userToken, userId, props.url]); + }, [userToken, isAuthenticated, props.url]); useEffect(() => { if (loaded && svg && containerRef.current) { diff --git a/frontend/frontend/src/pages/App/AppTrends.js b/frontend/frontend/src/pages/App/AppTrends.js index 0e58f649..783b573e 100644 --- a/frontend/frontend/src/pages/App/AppTrends.js +++ b/frontend/frontend/src/pages/App/AppTrends.js @@ -1,12 +1,7 @@ import React, { useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; -import { - BrowserRouter as Router, - Route, - Routes, - useParams, -} from 'react-router-dom'; +import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; import { logout as _logout, setUserAccess as _setUserAccess, @@ -20,11 +15,14 @@ import SettingsScreen from '../Settings'; import { NoMatchScreen } from '../Misc'; import { getUserMetadata } from '../../api'; import Footer from './Footer'; +import { + useIsAuthenticated, + useUserKey, +} from '../../redux/selectors/userSelectors'; function App() { - const userId = useSelector((state) => state.user.userId); - const userKey = useSelector((state) => state.user.userKey); - const isAuthenticated = userId && userId.length > 0; + const userKey = useUserKey(); + const isAuthenticated = useIsAuthenticated(); const dispatch = useDispatch(); const setUserAccess = (access) => diff --git a/frontend/frontend/src/pages/App/Header.js b/frontend/frontend/src/pages/App/Header.js index fc7f8b89..627d40e7 100644 --- a/frontend/frontend/src/pages/App/Header.js +++ b/frontend/frontend/src/pages/App/Header.js @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import PropTypes from 'prop-types'; import { Link } from 'react-router-dom'; @@ -8,6 +8,7 @@ import { GiHamburgerMenu as HamburgerIcon } from 'react-icons/gi'; import { MdSettings as SettingsIcon } from 'react-icons/md'; import { logout as _logout } from '../../redux/actions/userActions'; +import { useIsAuthenticated } from '../../redux/selectors/userSelectors'; import appIcon from '../../assets/appLogo64.png'; import { classnames } from '../../utils'; import { GITHUB_PUBLIC_AUTH_URL } from '../../constants'; @@ -61,8 +62,7 @@ MobileLink.defaultProps = defaultProps; const Header = ({ mode }) => { const [toggle, setToggle] = useState(false); - const userId = useSelector((state) => state.user.userId); - const isAuthenticated = userId && userId.length > 0; + const isAuthenticated = useIsAuthenticated(); const dispatch = useDispatch(); const logout = () => dispatch(_logout()); diff --git a/frontend/frontend/src/pages/Auth/SignUp.js b/frontend/frontend/src/pages/Auth/SignUp.js index a8d0d85e..56963f36 100644 --- a/frontend/frontend/src/pages/Auth/SignUp.js +++ b/frontend/frontend/src/pages/Auth/SignUp.js @@ -1,5 +1,4 @@ import React from 'react'; -import { useSelector } from 'react-redux'; import { FaGithub as GithubIcon } from 'react-icons/fa'; @@ -13,9 +12,6 @@ import { classnames } from '../../utils'; import mockup from '../../assets/mockup.png'; const SignUpScreen = () => { - // eslint-disable-next-line no-unused-vars - const userId = useSelector((state) => state.user.userId); - return (
diff --git a/frontend/frontend/src/pages/Home/Home.js b/frontend/frontend/src/pages/Home/Home.js index 9211d1dd..67a3cb1b 100644 --- a/frontend/frontend/src/pages/Home/Home.js +++ b/frontend/frontend/src/pages/Home/Home.js @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import BounceLoader from 'react-spinners/BounceLoader'; import { v4 as uuidv4 } from 'uuid'; @@ -19,14 +19,18 @@ import { CardTypes } from '../../utils'; import { DEFAULT_OPTION as STATS_DEFAULT_RANK } from '../../components/Home/StatsRankSection'; import { DEFAULT_OPTION as LANGUAGES_DEFAULT_LAYOUT } from '../../components/Home/LanguagesLayoutSection'; import { DEFAULT_OPTION as WAKATIME_DEFAULT_LAYOUT } from '../../components/Home/WakatimeLayoutSection'; +import { + useUserId, + useIsAuthenticated, + usePrivateAccess, +} from '../../redux/selectors/userSelectors'; const HomeScreen = () => { const [isLoading, setIsLoading] = useState(false); - const userId = useSelector((state) => state.user.userId); - const privateAccess = useSelector((state) => state.user.privateAccess); - - const isAuthenticated = userId && userId.length > 0; + const userId = useUserId(); + const privateAccess = usePrivateAccess(); + const isAuthenticated = useIsAuthenticated(); const dispatch = useDispatch(); diff --git a/frontend/frontend/src/pages/Home/stages/Login.js b/frontend/frontend/src/pages/Home/stages/Login.js index c163b7f4..e72ca576 100644 --- a/frontend/frontend/src/pages/Home/stages/Login.js +++ b/frontend/frontend/src/pages/Home/stages/Login.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { Button, Image } from '../../../components'; import { classnames } from '../../../utils'; @@ -13,12 +13,16 @@ import { } from '../../../constants'; import { FaGithub as GithubIcon } from 'react-icons/fa'; import { logout as _logout } from '../../../redux/actions/userActions'; +import { + useIsAuthenticated, + usePrivateAccess, + useUserKey, +} from '../../../redux/selectors/userSelectors'; const LoginStage = ({ setCurrItem }) => { - const userId = useSelector((state) => state.user.userId); - const userKey = useSelector((state) => state.user.userKey); - const privateAccess = useSelector((state) => state.user.privateAccess); - const isAuthenticated = userId && userId.length > 0; + const userKey = useUserKey(); + const privateAccess = usePrivateAccess(); + const isAuthenticated = useIsAuthenticated(); const dispatch = useDispatch(); const logout = () => { diff --git a/frontend/frontend/src/pages/Home/stages/SelectCard.js b/frontend/frontend/src/pages/Home/stages/SelectCard.js index 372010b0..d2d728b0 100644 --- a/frontend/frontend/src/pages/Home/stages/SelectCard.js +++ b/frontend/frontend/src/pages/Home/stages/SelectCard.js @@ -2,26 +2,26 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; import { Card } from '../../../components'; +import { useUserId } from '../../../redux/selectors/userSelectors'; const SelectCardStage = ({ selectedCard, setSelectedCard, setImageSrc }) => { - const userId = useSelector((state) => state.user.userId); + const userId = useUserId(); return (
{[ { title: 'GitHub Stats Card', description: 'your overall GitHub statistics', - imageSrc: `?username=${userId || 'anuraghazra'}`, + imageSrc: `?username=${userId}`, demoCustomization: '&include_all_commits=true', cardType: 'stats', }, { title: 'Top Languages Card', description: 'your most frequently used languages', - imageSrc: `/top-langs?username=${userId || 'anuraghazra'}`, + imageSrc: `/top-langs?username=${userId}`, demoCustomization: '&langs_count=4', cardType: 'top-langs', }, diff --git a/frontend/frontend/src/pages/Landing/Landing.js b/frontend/frontend/src/pages/Landing/Landing.js index 2a97c72c..96cce4a4 100644 --- a/frontend/frontend/src/pages/Landing/Landing.js +++ b/frontend/frontend/src/pages/Landing/Landing.js @@ -1,7 +1,6 @@ /* eslint-disable react/jsx-one-expression-per-line */ import React from 'react'; -import { useSelector } from 'react-redux'; import { Link } from 'react-router-dom'; import { FaCheck as CheckIcon, FaGithub as GithubIcon } from 'react-icons/fa'; @@ -13,11 +12,10 @@ import avgupta456Langs from '../../assets/avgupta456_langs.png'; import tiangoloRepos from '../../assets/tiangolo_repos.png'; import reininkRepos from '../../assets/reinink_repos.png'; import dhermesLangs from '../../assets/dhermes_langs.png'; +import { useIsAuthenticated } from '../../redux/selectors/userSelectors'; function LandingScreen() { - const userId = useSelector((state) => state.user.userId); - - const isAuthenticated = userId && userId.length > 0; + const isAuthenticated = useIsAuthenticated(); return (
diff --git a/frontend/frontend/src/pages/Settings/Settings.js b/frontend/frontend/src/pages/Settings/Settings.js index f2386066..3eba8b8f 100644 --- a/frontend/frontend/src/pages/Settings/Settings.js +++ b/frontend/frontend/src/pages/Settings/Settings.js @@ -1,9 +1,15 @@ import React, { useEffect, useRef, useState } from 'react'; import PropTypes from 'prop-types'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { Button } from '../../components'; import { logout as _logout } from '../../redux/actions/userActions'; +import { + useUserId, + useUserKey, + useIsAuthenticated, + usePrivateAccess, +} from '../../redux/selectors/userSelectors'; import { deleteAccount } from '../../api'; import { classnames } from '../../utils'; import { CLIENT_ID, GITHUB_PRIVATE_AUTH_URL, HOST } from '../../constants'; @@ -70,10 +76,10 @@ const SettingsScreen = () => { const wrapperRef = useRef(null); useOutsideAlerter(wrapperRef, closeDeleteModal); - const userId = useSelector((state) => state.user.userId); - const isAuthenticated = userId && userId.length > 0; - const userKey = useSelector((state) => state.user.userKey); - const privateAccess = useSelector((state) => state.user.privateAccess); + const userId = useUserId(); + const userKey = useUserKey(); + const isAuthenticated = useIsAuthenticated(); + const privateAccess = usePrivateAccess(); const accountTier = privateAccess ? 'Private Workflow' : 'Public Workflow'; const dispatch = useDispatch(); diff --git a/frontend/frontend/src/redux/selectors/userSelectors.js b/frontend/frontend/src/redux/selectors/userSelectors.js new file mode 100644 index 00000000..45f7bb8f --- /dev/null +++ b/frontend/frontend/src/redux/selectors/userSelectors.js @@ -0,0 +1,24 @@ +import { useSelector } from 'react-redux'; + +export const useUserId = () => { + return useSelector((state) => state.user.userId) || 'anuraghazra'; +}; + +export const useIsAuthenticated = () => { + return useSelector((state) => { + const userId = state.user.userId; + return userId && userId.length > 0; + }); +}; + +export const usePrivateAccess = () => { + return useSelector((state) => state.user.privateAccess); +}; + +export const useUserKey = () => { + return useSelector((state) => state.user.userKey); +}; + +export const useUserToken = () => { + return useSelector((state) => state.user.token); +};