Amphyx
Amphyx

Reputation: 755

Create a Theme Provider component in React Native

My application needs a color themes providing and I am trying to implement a Theme Providing component using a React Context, but it does not work. I can't figure out how to get the theme prop object from the Theme Provider's Context and how can I call the updateTheme function that is state of Theme Provider? This is my code:

ThemeProvider

import React from "react";
import { LightTheme, DarkTheme } from '../themes'

const Context = React.createContext();

export class ThemeProvider extends React.Component {
  
  state = {
    theme: LightTheme,
    updateTheme: (theme) => {
      this.setState({ theme: theme })
    }
  }

  render() {
    const { theme } = this.state
    return (
      <Context.Provider value={this.state} theme={theme} >
        { this.props.children }
      </Context.Provider>
    )
  }
}

export default ThemeProvider;

App

import React from 'react';
import {
  SafeAreaView,
  StyleSheet,
  ScrollView,
  View,
  Text,
  StatusBar,
} from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { HomeScreen } from './screens';
import { NavBar, ThemeProvider } from './components';

const Stack = createStackNavigator();

const App: () => React$Node = () => {

  return (
    <ThemeProvider>
      <NavigationContainer>
        <StatusBar barStyle="dark-content" />
        <NavBar />
        <Stack.Navigator
          screenOptions={{
            headerShown: false
          }}
        >
          <Stack.Screen name="Home" component={HomeScreen} />
        </Stack.Navigator>
      </NavigationContainer>
    </ThemeProvider>
  );
};

export default App;

Navbar (this component have to get the theme prop from the ThemeProvider to change it's color)

import React from 'react';
import {
  SafeAreaView,
  StyleSheet,
  ScrollView,
  View,
  Text,
  StatusBar,
} from 'react-native';

import { Container, Header, Left, Body, Right, Button, Title } from 'native-base';
import Icon from 'react-native-vector-icons/MaterialIcons';
import { Layout } from '../constants';

class NavBar extends React.Component {
  
  static contextType = ThemeProvider;
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <Header>
        <Left>
          <Button transparent>
            <Icon name='add' size={Layout.navIconSize} />
          </Button>
        </Left>
        <Body>
          <Title>Header</Title>
        </Body>
        <Right>
          <Button transparent>
            <Icon name='alarm' size={Layout.navIconSize} />
          </Button>
        </Right>
      </Header>
    )
  }
}

export default NavBar;

Themes

export const LightTheme = {
   dark: false,
   colors: {
      primary: 'rgb(255, 45, 85)',
      background: 'rgb(242, 242, 242)',
      card: 'rgb(255, 255, 255)',
      text: 'rgb(28, 28, 30)',
      border: 'rgb(199, 199, 204)',
      notification: 'rgb(255, 69, 58)',
   }
}

export const DarkTheme = {
   dark: true,
   colors: {
      primary: 'rgb(255, 45, 85)',
      background: 'rgb(0, 0, 0)',
      card: 'rgb(255, 255, 255)',
      text: 'rgb(28, 28, 30)',
      border: 'rgb(199, 199, 204)',
      notification: 'rgb(255, 69, 58)',
   },
};

Upvotes: 3

Views: 14316

Answers (1)

Guruparan Giritharan
Guruparan Giritharan

Reputation: 16334

You have done the major part, you just have to do some minor changes to get it to work.

First In order to change the values of a context you will have to export the context. Then access the consumer.

import React from "react";
import { LightTheme, DarkTheme } from '../themes'

export const Context = React.createContext();

export class ThemeProvider extends React.Component {
  
  state = {
    theme: LightTheme,
    updateTheme: (theme) => {
      this.setState({ theme: theme })
    }
  }

  render() {
    const { theme } = this.state
    return (
      <Context.Provider value={this.state} theme={theme} >
        { this.props.children }
      </Context.Provider>
    )
  }
}

export default ThemeProvider;

In the child component which is the NavBar in your case, you will have to import it and use the consumer to do updates Import the context like below (Your actual paths may vary)

import ThemeProvider, { Context } from './ThemeProvider';


class NavBar extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <Context.Consumer>
        {({ theme, updateTheme }) => (
          <Header>
            <Left>
              <Button
                title="Update Theme"
                onPress={() => updateTheme(theme.dark ? LightTheme : DarkTheme)}
              />
              <Button transparent>
                <Icon name="add" size={Layout.navIconSize} />
              </Button>
            </Left>
            <Body>
              <Title>Header</Title>
            </Body>
            <Right>
              <Button transparent>
                <Icon name="alarm" size={Layout.navIconSize} />
              </Button>
            </Right>
          </Header>
        )}
      </Context.Consumer>
    );
  }
}

{({ theme, updateTheme }) => ( The above line provides access to your theme value in the context and updateTheme. You can try out the code.

Upvotes: 3

Related Questions