0

I want to implement dark mode and black mode in my app and the way I have it is that the user toggles on dark/black mode from one class in which I want the state to be updated to all the classes, the toggle class is as followed:

AppearanceToggle Class

  state = {
    BlackModeValue: null,
    DarkModeValue: null
  };
  componentDidMount = () => {
    AsyncStorage.getItem('DarkModeValue').then(value => this.setState({ DarkModeValue: JSON.parse(value) }));
    AsyncStorage.getItem('BlackModeValue').then(value => this.setState({ BlackModeValue: JSON.parse(value) }));
  };

  //AsyncStorage.setItem .........

  render() {
    return (

      <ScrollView style={[ styles.View , this.state.DarkModeValue ?  darkmode.ASView : null || this.state.BlackModeValue ? blackmode.ASView : null ]}>
          <SettingsList borderColor='#c8c7cc' defaultItemSize={50}>              
             <SettingsList.Item
              hasSwitch={true}
              switchState={this.state.DarkModeValue}
              switchOnValueChange={//Goes to asyncStorage.setItem method}
              title='Dark Mode'
            />
           <SettingsList.Item
             hasSwitch={true}
             switchState={this.state.BlackModeValue}
             switchOnValueChange={//Goes to asyncStorage.setItem method}
             title='Black Mode'
           />
          </SettingsList>
      </ScrollView>
    );
  }
}

And then in the class (which is SettingsScreen.js, this is the screen that navigates to AppearanceToggle ) that I want to .getItem and change the state is as followed:

  state = {
      switchValue: false,
      rated: false,
      DarkModeValue:null,
      BlackModeValue:null,
    };
  componentDidMount = () => {
    AsyncStorage.getItem('DarkModeValue').then(value => this.setState({ DarkModeValue: JSON.parse(value) }));
    AsyncStorage.getItem('BlackModeValue').then(value => this.setState({ BlackModeValue: JSON.parse(value) }));
  };
  render() {
    return (
      <ScrollView style={[ styles.View , this.state.DarkModeValue ?  styles.DMView : null || this.state.BlackModeValue ? styles.BMView : null ]}>
        ..........
       </ScrollView>

The problem I have is that when I change the switch, it affects the AppearanceToggleScreen Class instantly but not the SettingsScreen UNLESS I refresh the app. Is there a way to do it so all of them get affected instantly?

Noe Duran
  • 77
  • 11

2 Answers2

2

Perhaps the best way to propagate it is to listen for the changes in your AppComponent using Context or root component. e.g.

So you would create a theme context like :

export const themes = {
  blackMode: {
    foreground: '#000000',
    background: '#eeeeee',
  },
  darkMode: {
    foreground: '#2f4f4ff',
    background: '#222222',
  },
};

export const ThemeContext = React.createContext(
  themes.darkMode // default value
)

;

Your AppearanceToggle class would have something like :

import {ThemeContext} from './theme-context';

class ThemedButton extends Component {
  render() {
    let props = this.props;
    let theme = this.context;
    return (
      <button
        {...props}
        style={{backgroundColor: theme.background}}
      />
    );
  }
}
ThemedButton.contextType = ThemeContext;

export default ThemedButton;

And then your AppComponent could be

    import {ThemeContext, themes} from './theme-context';
    import ThemedButton from './themed-button';



    function Toolbar(props) {
      // Render your customized toolbar here and bind the changeTheme function to it
    }


class App extends Component {
  constructor(props) {
    super(props);
   };

  componentDidMount = () => {
     AsyncStorage.getItem('selectedTheme').then(value => this.setState({ selectedTheme: JSON.parse(value) }));
  };


    this.toggleTheme = () => {
      this.setState(state => ({
        theme:
          state.theme === themes.darkMode
            ? themes.blackMode
            : themes.darkMode,
      }));
    };
  }

  render() {
    // The ThemedButton button inside the ThemeProvider
    // uses the theme from state while the one outside uses
    // the default dark theme
    return (
      <Page>
        <ThemeContext.Provider value={this.state.theme}>
          <Toolbar changeTheme={this.toggleTheme} />
        </ThemeContext.Provider>
        <Section>
          <ThemedButton />
        </Section>
      </Page>
    );
  }
}

For more read

Chad Nehemiah
  • 859
  • 6
  • 15
1

The thing is that in the AppearanceToggleScreen you're changing the state, therefore the component is rerendered (with the new theme), but because the SettingsScreen is already in the navigation stack (because that's where you're navigating from) the componentDidMount is not executing again.

Now, maybe you want to use the Context API to access globally to the values, or do something like this.

Edwin Vargas
  • 1,132
  • 1
  • 8
  • 14