Closed mauroerta closed 2 years ago
Whenever the React
team will release react@18.0.0
the possible implementation of this could be something like the following:
inside @morfeo/hooks package
import { morfeo, theme as themeHandler, ThemeName } from '@morfeo/core';
import { useSyncExternalStore } from 'react';
function subscribe(...callback: Parameters<typeof themeHandler.subscribe>) {
const uid = themeHandler.subscribe(...callback);
return () => themeHandler.cleanUp(uid);
}
export function useMorfeo() {
const theme = useSyncExternalStore(subscribe, morfeo.getTheme);
const current = morfeo.getCurrentTheme();
function setCurrent(themeName: ThemeName) {
morfeo.setCurrentTheme(themeName);
}
return { theme, current, setCurrent, subscribe };
}
To test if this code is working, I think this simple test would prove the correct behavior:
import { renderHook, act } from '@testing-library/react';
import { morfeo, Theme, ThemeName } from '@morfeo/core';
import { useMorfeo } from '../src/useMorfeo';
const LIGHT_THEME = {
colors: {
primary: 'black',
secondary: 'white',
},
} as Theme;
const DARK_THEME = {
colors: {
primary: 'white',
secondary: 'black',
},
} as Theme;
const LIGHT_THEME_KEY = 'light' as ThemeName;
const DARK_THEME_KEY = 'dark' as ThemeName;
beforeAll(() => {
morfeo.setTheme(LIGHT_THEME_KEY, LIGHT_THEME);
morfeo.setTheme(DARK_THEME_KEY, DARK_THEME);
morfeo.setCurrentTheme(LIGHT_THEME_KEY);
});
describe('useMorfeo', () => {
it('should return the theme', () => {
const { result } = renderHook(() => useMorfeo());
expect(result.current.theme).toEqual(LIGHT_THEME);
});
describe('when the theme changes', () => {
it('should return the dark theme', () => {
const { result } = renderHook(() => useMorfeo());
act(() => {
result.current.setCurrent(DARK_THEME_KEY);
});
expect(result.current.theme).toEqual(DARK_THEME);
});
});
});
In case everything works, we should also think about some other changes, for example:
Currently, this provider is used to force a re-render when the theme changes, since useSyncExternalStore
has been introduced exactly for this reason, we can remove MorfeoProvider
and use the hook useMorfeo
inside the other hooks (such as useTheme
, useStyle
and so on) to force a re-render when needed.
Another option is to keep MorfeoProvider
and use the hook useMorfeo
(or at least useSyncExternalStore) only in the provider and leave all the other hooks as they are (no breaking changes at all)
🎉 React 18.0.0 is OUT 🎉
I already tried the snippet in the previous comment and seems to work.
Before opening a PR I'll wait for some other updates in other dependencies like @testing-librty/react
, @types/react
, and so on...
Anyway, PRs are welcome if anyone wants to try to make this update!
Morfeo is born to work everywhere, it already works with
React
,Svelte
,Angular
or any other JS framework. Anyway for any of these framework we usually write packages to make the integration as good as possible, for example withdirectives
for svelte orhook
for react.React v18 introduced a new hook called
useSyncExternalStore
, this hook helps to sync external states with the react state. It seems to be the perfect solution to always be sure that the theme used (and returned) by the hooks or@morfeo/hooks
is the current used inside themorfeo
instance.It will probably unlock even other functionalities like:
cuncurrent mode
compatibility (not sure it's not working right now)References:
Before proceeding with this issue, we need to upgrade to React 18. The upgrade to React 18 is covered by this issue