React Native(或React)在单独的ts模块中监听暗模式的样式变化

React Native (or React) listen to the style changes for the dark mode in separate ts modules

目标:在 React Native 应用程序中实现 暗模式。 系统简介: 文件结构:

在我的应用程序中,为了将样式与组件分开,我将它们移动到 .ts 文件中并导出为模块。 StyleCss.ts的示例:

import {StyleSheet} from 'react-native';
import {Window, FixedCSS, hsize, wsize} from '../../entities/constants';
    
export default StyleSheet.create({
    loading: {
        backgroundColor: FixedCSS.Colors.MainBackgroundColor,
    }
    ...
});

存储在 constants.ts 中的所有与样式相关的常量值,例如颜色、行高等。还有 constants.ts 包含一个名为 FixedCSS 的 class,它具有静态属性:

export class FixedCSS {
    static isDark = true;

    static Colors = {
        // Main Colors
        RootColor: FixedCSS.isDark ? '#FF6666' : '#FF6666',
        MainBackgroundColor: FixedCSS.isDark ? '#050505' : '#FFFFFF',
        ...
    }
    ...
}

isDark 属性 FixedCSS 应该借助 Profile.ts

中的 Switch 组件进行更改
<Switch
    value={darkMode}
    onValueChange={value => {
        this.setState({darkMode: value});
    }}
/>

问题:如何修改FixedCSS class中的isDark属性,这样会影响组件的外观。最有问题的方面是 StyleCSS.ts 文件不会反映 FixedCSS class 中 isDark 属性 的变化,因为它们仅在以下情况下导出一次js 包被创建。

更改 class 的 static 属性 不会触发重新呈现的方式。这需要以某种方式利用 state 。我推荐一个主题Context。您可以使用一个上下文或单独的上下文来控制 isDarkMode 并提供 CSS,这就是我在这里所做的。

为了仅导出一个值一次并使该值保持最新,导出的值可以是函数而不是常量。我正在使固定 CSS 成为 isDark 的函数,并且各个组件样式成为 FixedCSS.

的函数

constants.ts

export const createFixedCSS = (isDark: boolean) => ({
  isDark,
  Colors: {
    // Main Colors
    RootColor: isDark ? "#FF6666" : "#FF6666",
    MainBackgroundColor: isDark ? "#050505" : "#FFFFFF"
    ...
  }
  ...
});

export type FixedCSS = ReturnType<typeof createFixedCSS>;

export const IS_DARK_DEFAULT = false;

cssContext.ts

// context to provide the FixedCSS object, which also includes isDark
const FixedCSSContext = createContext(createFixedCSS(IS_DARK_DEFAULT));

// context to provide a function for switching modes
// type Dispatch<SetStateAction<T>> allows for setting based on previous value
type SetModeContextValue = Dispatch<SetStateAction<boolean>>;

// needs an initial value that fits the type signature
const SetModeContext = createContext<SetModeContextValue>(() => {
    throw new Error(
      "cannot set `isDark` outside of a `DarkModeContext` Provider"
    );
});

// combine both contexts into one Provider component
export const CSSProvider: FC<{}> = ({ children }) => {
  // store the isDark state
  const [isDark, setIsDark] = useState(IS_DARK_DEFAULT);

  // update the FixedCSS when isDark changes
  // I'm not sure if memoizing is actually necessary here?
  const FixedCSS = useMemo(() => createFixedCSS(isDark), [isDark]);

  return (
    <SetModeContext.Provider value={setIsDark}>
      <FixedCSSContext.Provider value={FixedCSS}>
        {children}
      </FixedCSSContext.Provider>
    </SetModeContext.Provider>
  );
};

// I prefer to export the hooks rather than the contexts themselves
export const useFixedCSS = () => useContext(FixedCSSContext);
export const useSetMode = () => useContext(SetModeContext);

// helper hook for `StyleCss.ts` files
// takes the makeStyles factory function and turns it into a style object by accessing the  FixedCSS context
export const useStyleFactory = <T extends StyleSheet.NamedStyles<T>>(
  factory: (css: FixedCSS) => T
): T => {
  const FixedCSS = useFixedCSS();
  return useMemo(() => factory(FixedCSS), [FixedCSS, factory]);
};

ProfileCss.ts

export const makeStyles = (FixedCSS: FixedCSS) => StyleSheet.create({
    loading: {
        backgroundColor: FixedCSS.Colors.MainBackgroundColor,
    }
});

Profile.ts

const DarkModeToggle = () => {
  // access isDark from global FixedCSS
  const {isDark} = useFixedCSS();
  
  // access update callback
  const setIsDark = useSetMode();

  return (
    <Switch
      value={isDark}
      onValueChange={setIsDark}
    />
  )
}

const Profile = () => {

  // pass the imported `makeStyles` to the hook and get stateful styles object
  const styles = useStyleFactory(makeStyles);

  return (
    <View>
      <DarkModeToggle/>
      <View style={styles.loading}/>
    </View>
  );
};

CodeSandbox Demo(全部在一个文件中)