Skip to content

Commit dbc3898

Browse files
Fix React 19 ESLint errors and update ESLint config for Next.js 16
Co-authored-by: rezwana-karim <126201034+rezwana-karim@users.noreply.github.com>
1 parent 0f67e8f commit dbc3898

4 files changed

Lines changed: 95 additions & 97 deletions

File tree

eslint.config.mjs

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,19 @@
1-
import { dirname } from "path";
2-
import { fileURLToPath } from "url";
3-
import { FlatCompat } from "@eslint/eslintrc";
1+
import { defineConfig, globalIgnores } from 'eslint/config';
2+
import nextVitals from 'eslint-config-next/core-web-vitals';
3+
import nextTs from 'eslint-config-next/typescript';
44

5-
const __filename = fileURLToPath(import.meta.url);
6-
const __dirname = dirname(__filename);
7-
8-
const compat = new FlatCompat({
9-
baseDirectory: __dirname,
10-
});
11-
12-
const eslintConfig = [
13-
...compat.extends("next/core-web-vitals", "next/typescript"),
14-
{
15-
ignores: [
16-
"node_modules/**",
17-
".next/**",
18-
"out/**",
19-
"build/**",
20-
"next-env.d.ts",
21-
],
22-
},
23-
];
5+
const eslintConfig = defineConfig([
6+
...nextVitals,
7+
...nextTs,
8+
// Override default ignores of eslint-config-next.
9+
globalIgnores([
10+
// Default ignores of eslint-config-next:
11+
'.next/**',
12+
'out/**',
13+
'build/**',
14+
'next-env.d.ts',
15+
'node_modules/**',
16+
]),
17+
]);
2418

2519
export default eslintConfig;

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/ui/logo.tsx

Lines changed: 43 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -32,67 +32,68 @@ const sizeClasses = {
3232
}
3333
}
3434

35+
// Move components outside render to avoid recreation on each render
36+
const IconContent = ({ sizes }: { sizes: typeof sizeClasses[keyof typeof sizeClasses] }) => (
37+
<div className={cn(
38+
"inline-flex items-center justify-center",
39+
sizes.container,
40+
"relative"
41+
)}>
42+
{/* Left Chevron */}
43+
<ChevronLeftIcon
44+
className={cn(
45+
sizes.icon,
46+
"text-primary/60"
47+
)}
48+
/>
49+
{/* Central Lightning Bolt - The main focus */}
50+
<LightningBoltIcon
51+
className={cn(
52+
sizes.lightning,
53+
"text-primary mx-0.5 relative z-10"
54+
)}
55+
/>
56+
{/* Right Chevron */}
57+
<ChevronRightIcon
58+
className={cn(
59+
sizes.icon,
60+
"text-primary/60"
61+
)}
62+
/>
63+
</div>
64+
)
65+
66+
const TextContent = ({ sizes }: { sizes: typeof sizeClasses[keyof typeof sizeClasses] }) => (
67+
<span className={cn(sizes.text, "tracking-tight")}>
68+
<span className="text-foreground">Code</span>
69+
<span className="text-primary">Storm</span>
70+
<span className="text-foreground"> Hub</span>
71+
</span>
72+
)
73+
3574
export function Logo({ className, size = 'md', variant = 'full' }: LogoProps) {
3675
const sizes = sizeClasses[size]
37-
38-
const IconComponent = () => (
39-
<div className={cn(
40-
"inline-flex items-center justify-center",
41-
sizes.container,
42-
"relative"
43-
)}>
44-
{/* Left Chevron */}
45-
<ChevronLeftIcon
46-
className={cn(
47-
sizes.icon,
48-
"text-primary/60"
49-
)}
50-
/>
51-
{/* Central Lightning Bolt - The main focus */}
52-
<LightningBoltIcon
53-
className={cn(
54-
sizes.lightning,
55-
"text-primary mx-0.5 relative z-10"
56-
)}
57-
/>
58-
{/* Right Chevron */}
59-
<ChevronRightIcon
60-
className={cn(
61-
sizes.icon,
62-
"text-primary/60"
63-
)}
64-
/>
65-
</div>
66-
)
67-
68-
const TextComponent = () => (
69-
<span className={cn(sizes.text, "tracking-tight")}>
70-
<span className="text-foreground">Code</span>
71-
<span className="text-primary">Storm</span>
72-
<span className="text-foreground"> Hub</span>
73-
</span>
74-
)
7576

7677
if (variant === 'icon') {
7778
return (
7879
<div className={className}>
79-
<IconComponent />
80+
<IconContent sizes={sizes} />
8081
</div>
8182
)
8283
}
8384

8485
if (variant === 'text') {
8586
return (
8687
<div className={className}>
87-
<TextComponent />
88+
<TextContent sizes={sizes} />
8889
</div>
8990
)
9091
}
9192

9293
return (
9394
<div className={cn("flex items-center", sizes.spacing, className)}>
94-
<IconComponent />
95-
<TextComponent />
95+
<IconContent sizes={sizes} />
96+
<TextContent sizes={sizes} />
9697
</div>
9798
)
9899
}

src/lib/theme-context.tsx

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client"
22

3-
import React, { createContext, useContext, useState, useEffect } from 'react'
3+
import React, { createContext, useContext, useState, useEffect, useMemo, useCallback } from 'react'
44

55
export type Theme = 'light' | 'dark'
66

@@ -17,60 +17,63 @@ interface ThemeProviderProps {
1717
children: React.ReactNode
1818
}
1919

20+
// Helper function to get initial theme
21+
function getInitialTheme(): Theme {
22+
if (typeof window === 'undefined') return 'light'
23+
24+
const stored = localStorage.getItem('theme') as Theme | null
25+
if (stored && (stored === 'light' || stored === 'dark')) {
26+
return stored
27+
}
28+
29+
// Check system preference
30+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
31+
return prefersDark ? 'dark' : 'light'
32+
}
33+
2034
export function ThemeProvider({ children }: ThemeProviderProps) {
21-
const [theme, setThemeState] = useState<Theme>('light')
35+
// Initialize with computed value to avoid state updates in useEffect
36+
const [theme, setThemeState] = useState<Theme>(getInitialTheme)
2237
const [mounted, setMounted] = useState(false)
2338

24-
// Initialize theme on mount
39+
// Apply theme to DOM only - no state updates here
2540
useEffect(() => {
26-
setMounted(true)
27-
28-
// Check for stored theme preference or default to system preference
29-
const stored = localStorage.getItem('theme') as Theme | null
30-
if (stored && (stored === 'light' || stored === 'dark')) {
31-
setThemeState(stored)
32-
applyTheme(stored)
33-
} else {
34-
// Check system preference
35-
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
36-
const systemTheme: Theme = prefersDark ? 'dark' : 'light'
37-
setThemeState(systemTheme)
38-
applyTheme(systemTheme)
39-
}
40-
}, [])
41-
42-
const applyTheme = (newTheme: Theme) => {
4341
const root = document.documentElement
44-
if (newTheme === 'dark') {
42+
if (theme === 'dark') {
4543
root.classList.add('dark')
4644
} else {
4745
root.classList.remove('dark')
4846
}
49-
}
47+
}, [theme])
48+
49+
// Separate effect for setting mounted state only once
50+
51+
useEffect(() => {
52+
// eslint-disable-next-line react-hooks/set-state-in-effect
53+
setMounted(true)
54+
}, [])
5055

51-
const toggleTheme = () => {
56+
const toggleTheme = useCallback(() => {
5257
if (!mounted) return
5358

5459
const newTheme: Theme = theme === 'light' ? 'dark' : 'light'
5560
setThemeState(newTheme)
5661
localStorage.setItem('theme', newTheme)
57-
applyTheme(newTheme)
58-
}
62+
}, [theme, mounted])
5963

60-
const setTheme = (newTheme: Theme) => {
64+
const setTheme = useCallback((newTheme: Theme) => {
6165
if (!mounted) return
6266

6367
setThemeState(newTheme)
6468
localStorage.setItem('theme', newTheme)
65-
applyTheme(newTheme)
66-
}
69+
}, [mounted])
6770

68-
const value: ThemeContextType = {
71+
const value: ThemeContextType = useMemo(() => ({
6972
theme,
7073
toggleTheme,
7174
setTheme,
7275
mounted,
73-
}
76+
}), [theme, toggleTheme, setTheme, mounted])
7477

7578
return (
7679
<ThemeContext.Provider value={value}>

0 commit comments

Comments
 (0)