简体   繁体   English

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

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

Goal: Implement dark mode in react native application.目标:在反应原生应用程序中实现暗模式 A little introduction to the system: File structure:系统简介: 文件结构:

  • Profile.ts配置文件.ts
  • ProfileCss.ts ProfileCss.ts
  • constants.ts常量.ts

In my application in order to separate styles from components, I moved them into.ts files and exported as modules.在我的应用程序中,为了将 styles 与组件分开,我将它们移动到 .ts 文件中并作为模块导出。 Example of StyleCss.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,
    }
    ...
});

All style-related constant values stored in constants.ts such as Colors, LineHeights, and etc. Also constants.ts contains a class called FixedCSS which has static properties:所有与样式相关的常量值都存储在constants.ts中,例如 Colors、LineHeights 等。 constants.ts还包含一个名为FixedCSS的 class,它具有 ZA81259CEF8E959C624DF1D456E5D3297

export class FixedCSS {
    static isDark = true;

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

isDark property of FixedCSS is supposed to be changed with the help of Switch component in Profile.ts FixedCSS的 isDark 属性应该在 Profile.ts 中的 Switch 组件的帮助下进行更改

<Switch
    value={darkMode}
    onValueChange={value => {
        this.setState({darkMode: value});
    }}
/>

The problem: How can I change isDark property in FixedCSS class so that it will affect the appearance of components.问题:如何更改FixedCSS class中的 isDark 属性,使其影响组件的外观。 The most problematic aspect is that StyleCSS.ts files do not reflect to the change of isDark property in FixedCSS class, as they are exported only once when js bundle gets created.最有问题的方面是 StyleCSS.ts 文件不会反映 FixedCSS class 中isDark属性的更改,因为它们仅在创建 js 包时导出一次。

Changing a static property of a class is not going to trigger re-renders the way that it's supposed to.更改 class 的static属性不会触发按预期方式重新渲染。 This needs to make use of state somehow.这需要以某种方式使用state I recommend a theme Context .我推荐一个主题Context You could use one context or have separate contexts for controlling isDarkMode and providing the CSS, which is what I am doing here.您可以使用一个上下文或使用单独的上下文来控制isDarkMode并提供 CSS,这就是我在这里所做的。

In order to export a value only once but have that value stay current, the exported value can be a function rather than a constant.为了只导出一次值但使该值保持当前状态,导出的值可以是 function 而不是常数。 I am making the fixed CSS be a function of isDark and the individual component styles be a function of FixedCSS . I am making the fixed CSS be a function of isDark and the individual component styles be a function of FixedCSS .

constants.ts常量.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 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 ProfileCss.ts

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

Profile.ts配置文件.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 (all in one file) CodeSandbox 演示(全部在一个文件中)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM