Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use useSyncExternalStore to sync morfeo's theme with react #86

Closed
mauroerta opened this issue Oct 20, 2021 · 2 comments · Fixed by #254
Closed

Use useSyncExternalStore to sync morfeo's theme with react #86

mauroerta opened this issue Oct 20, 2021 · 2 comments · Fixed by #254
Assignees
Labels
enhancement New feature or request good first issue Good for newcomers

Comments

@mauroerta
Copy link
Collaborator

mauroerta commented Oct 20, 2021

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 with directives for svelte or hook 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 the morfeo instance.

It will probably unlock even other functionalities like:

  • smart re-render (only if values used by the component changes and not every time the theme changes)
  • 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

@mauroerta mauroerta added enhancement New feature or request help wanted Extra attention is needed labels Oct 20, 2021
@mauroerta
Copy link
Collaborator Author

mauroerta commented Feb 9, 2022

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:

MorfeoProvider

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)

@mauroerta mauroerta pinned this issue Feb 9, 2022
@mauroerta
Copy link
Collaborator Author

🎉 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!

@mauroerta mauroerta self-assigned this Apr 12, 2022
@mauroerta mauroerta removed the help wanted Extra attention is needed label Apr 12, 2022
@mauroerta mauroerta added the good first issue Good for newcomers label May 27, 2022
mauroerta added a commit that referenced this issue May 29, 2022
use `useSyncExternalStore` to sync morfeo with the React render.
Removed `MorfeoProvider` / `MorfeoContext` and introduced `useMorfeo`

Closes #86
@mauroerta mauroerta mentioned this issue May 29, 2022
11 tasks
mauroerta added a commit that referenced this issue May 30, 2022
use `useSyncExternalStore` to sync morfeo with the React render.
Removed `MorfeoProvider` / `MorfeoContext` and introduced `useMorfeo`

Closes #86
mauroerta added a commit that referenced this issue May 30, 2022
use `useSyncExternalStore` to sync morfeo with the React render.
Removed `MorfeoProvider` / `MorfeoContext` and introduced `useMorfeo`

Closes #86
mauroerta added a commit that referenced this issue May 30, 2022
mauroerta added a commit that referenced this issue May 30, 2022
use `useSyncExternalStore` to sync morfeo with the React render.
Removed `MorfeoProvider` / `MorfeoContext` and introduced `useMorfeo`

Closes #86
mauroerta added a commit that referenced this issue May 30, 2022
mauroerta added a commit that referenced this issue May 30, 2022
use `useSyncExternalStore` to sync morfeo with the React render.
Removed `MorfeoProvider` / `MorfeoContext` and introduced `useMorfeo`

Closes #86
mauroerta added a commit that referenced this issue May 30, 2022
mauroerta added a commit that referenced this issue May 30, 2022
@mauroerta mauroerta unpinned this issue May 30, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant