Skip to content

Commit 2aabd37

Browse files
committed
feat: useTheme add onChange callback
1 parent 87f8076 commit 2aabd37

File tree

4 files changed

+30
-22
lines changed

4 files changed

+30
-22
lines changed

jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ module.exports = {
1414
testPathIgnorePatterns: ['/.history/'],
1515
modulePathIgnorePatterns: ['<rootDir>/package.json'],
1616
resetMocks: false,
17-
setupFiles: ['./jest.setup.js', 'jest-localstorage-mock'],
17+
setupFiles: ['./jest.setup.js', 'jest-localstorage-mock', './match-media-mock.js'],
1818
setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'],
1919
transform: {
2020
'^.+\\.tsx?$': ['ts-jest', { tsconfig: 'tsconfig.json' }],

match-media-mock.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Object.defineProperty(window, 'matchMedia', {
2+
writable: true,
3+
value: jest.fn().mockImplementation((query) => ({
4+
matches: false,
5+
media: query,
6+
onchange: null,
7+
addListener: jest.fn(), // deprecated
8+
removeListener: jest.fn(), // deprecated
9+
addEventListener: jest.fn(),
10+
removeEventListener: jest.fn(),
11+
dispatchEvent: jest.fn(),
12+
})),
13+
});

packages/hooks/src/useTheme/__test__/index.test.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,5 @@
1-
Object.defineProperty(window, 'matchMedia', {
2-
writable: true,
3-
value: jest.fn().mockImplementation((query) => ({
4-
matches: false,
5-
media: query,
6-
onchange: null,
7-
addListener: jest.fn(), // deprecated
8-
removeListener: jest.fn(), // deprecated
9-
addEventListener: jest.fn(),
10-
removeEventListener: jest.fn(),
11-
dispatchEvent: jest.fn(),
12-
})),
13-
});
14-
151
import { act, renderHook } from '@testing-library/react';
16-
import { useTheme } from '../index';
2+
import useTheme from '../index';
173

184
describe('useTheme', () => {
195
test('themeMode init', () => {

packages/hooks/src/useTheme/index.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,27 @@ export enum ThemeMode {
99

1010
export type ThemeModeType = `${ThemeMode}`;
1111

12+
export type ThemeType = 'light' | 'dark';
13+
1214
const matchMedia = window.matchMedia('(prefers-color-scheme: dark)');
1315

14-
function useCurrentTheme() {
15-
const [theme, setTheme] = useState<'light' | 'dark'>(() => {
16-
return matchMedia.matches ? ThemeMode.DARK : ThemeMode.LIGHT;
16+
type Callback = (theme: ThemeType) => void;
17+
18+
function useCurrentTheme(callback: Callback = () => {}) {
19+
const [theme, setTheme] = useState<ThemeType>(() => {
20+
const init = matchMedia.matches ? ThemeMode.DARK : ThemeMode.LIGHT;
21+
callback(init);
22+
return init;
1723
});
1824

1925
useEffect(() => {
2026
const onThemeChange: MediaQueryList['onchange'] = (event) => {
2127
if (event.matches) {
2228
setTheme(ThemeMode.DARK);
29+
callback(ThemeMode.DARK);
2330
} else {
2431
setTheme(ThemeMode.LIGHT);
32+
callback(ThemeMode.LIGHT);
2533
}
2634
};
2735

@@ -30,17 +38,18 @@ function useCurrentTheme() {
3038
return () => {
3139
matchMedia.removeEventListener('change', onThemeChange);
3240
};
33-
}, []);
41+
}, [callback]);
3442

3543
return theme;
3644
}
3745

3846
type Options = {
3947
localStorageKey?: string;
48+
onChange?: Callback;
4049
};
4150

4251
export default function useTheme(options: Options = {}) {
43-
const { localStorageKey } = options;
52+
const { localStorageKey, onChange } = options;
4453

4554
const [themeMode, setThemeMode] = useState<ThemeModeType>(() => {
4655
const preferredThemeMode =
@@ -57,7 +66,7 @@ export default function useTheme(options: Options = {}) {
5766
}
5867
};
5968

60-
const currentTheme = useCurrentTheme();
69+
const currentTheme = useCurrentTheme(onChange);
6170
const theme = themeMode === ThemeMode.SYSTEM ? currentTheme : themeMode;
6271

6372
return {

0 commit comments

Comments
 (0)