React-native-ui-kitten: [feature request] useTheme hook

Created on 15 Oct 2019  路  11Comments  路  Source: akveo/react-native-ui-kitten

Issue type

I'm submitting a ...

  • [ ] bug report
  • [x] feature request

Issue description

Current behavior:
Not implemented yet.

Expected behavior:
Exposing a custom hook useTheme to access to theme context value.

Using this hook we can be able to access the current value theme in this manner:

e.x:

import React from 'react';
import { View } from 'react-native';
import { useTheme } from 'themes';

function MyComponent() {
  const themeValue = useTheme();
  const backgroundColor = React.useMemo(
    () =>
      themeValue === 'light'
        ? { backgroundColor: 'blue' }
        : { backgroundColor: 'darkblue' },
    [themeValue]
  );

  return <View style={[backgroundColor]}></View>;
}

Related code:

Adding a useContext consuming ThemeContext.

export function useTheme(): ThemeType {
  const themeContext = useContext(ThemeContext);

  if (isEmpty(themeContext)) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }

  return themeContext;
}

Other information:

I have a fork with useTheme implemented with tests, but im not able to start expo app -.-
It's not finished yet but if maintainers are agree and see useful this feature im glad to keep working on this pr. link to repo with useTheme: https://github.com/x0s3/react-native-ui-kitten

Im open to talk else feel free to close this feature request :)

Proposal Components

Most helpful comment

@henoktsegaye when using withStyles in component you can do

const backgroundColor = props.themedStyle.container.backgroundColor;

All 11 comments

Hi! Thanks for advice 馃憤 We'll definitely think about this. Did you know we have withStyles HOC? This will also add theme property in wrapped component.

import { withStyles } from 'react-native-ui-kitten';

const MyComponent = (props) => (
  <View style={{ backgroundColor: props.theme['color-primary-default'] }} />
);

export const MyThemedComponent = withStyles(MyComponent);

Btw to launch playground app in dev mode consider following in this guide

P.S: Just seen we forgot to mention the feature described above in docs :)

Hi,
I think useStyles is also worth to be implemented.

I used some of yours internal exports, hopefully that's ok.

import { useContext, useMemo } from 'react';
import { StyleType, ThemedStyleType, ThemeType } from 'react-native-ui-kitten';
import { createThemedStyle } from 'react-native-ui-kitten/theme/style/style.service';
import { ThemeContext } from 'react-native-ui-kitten/theme/theme/themeContext';

const createThemedStyles = (style: ThemedStyleType, theme: ThemeType): ThemedStyleType => {
  return Object.keys(style).reduce((acc: StyleType, current: string): ThemedStyleType => {
    return { ...acc, [current]: createThemedStyle(style[current], theme) };
  }, {});
};

export type CreateStylesFunction<T> = (theme: ThemeType) => T;

export function useStyles<T>(createStyles?: CreateStylesFunction<T>): T {
  const theme = useContext(ThemeContext);

  if (!theme) {
    throw new Error('useStyles must be used within a ThemeProvider');
  }

  return useMemo(() => {
    const style: ThemedStyleType = createStyles ? createStyles(theme) : {};
    return createThemedStyles(style, theme);
  }, [createStyles, theme]);
}

usage:

export const Dashboard = ({ style, ...props }) => {
  const styles = useStyles(themedStyle);

  return (
    <Layout {...props} style={[styles.dashboard, style]}/>
  );
};

const themedStyle = theme => ({
  dashboard: {
    backgroundColor: theme['color-danger-disabled'],
  },
});

I would appreciate if you point me on possible errors.

@Veikedo your code does the same, but it memoizes style object rather than passing it to props like it does withStyles. For now, consider using withStyles like it is described in example above. In future, I guess, we can think about renaming this feature :)

@artyorsh yeah, it's the idea to do the same but with hooks rather than HOC.

Btw, why not use a plain object instead of CreateStylesFunction ?
eg:

export const ThemedButton = withStyles(Button, {
  backgroundColor: 'color-primary-default',
});

vs current:

export const ThemedButton = withStyles(Button, (theme) => ({
  backgroundColor: theme['color-primary-default'],
}));

is it to make it lazy?

@Veikedo nice notice. It's in our backlog. We currently think about simplifying this APIs, and appreciate any ideas.

Your solution has one lack: in case theme contains more parameters: not only colors but global border-radius etc (sometimes it will) - there is no way to get it from theme

@Veikedo We also have already some scripts to generate typescript interfaces for theme, component parameters and other Eva-based things, so it can make work autocomplete in your IDE, but it is also a beta feature

I also want to answer one question that you probably might have:
We use dash-case for naming variables because we also have a web framework that is also based on Eva Design System :)

'color-primary-default'

This, unfortunately, doesn't work for my case.
its outputting $color-primary-500

@henoktsegaye you should use withStyles when accepting theme values

import { withStyles } from '@ui-kitten/components';

const ThemedComponent = withStyles(MyComponent, theme => ({
  container: {
    backgroundColor: theme['color-primary-default'],
  },
}));

@artyorsh I don't want to put it in a style, I want to have it as a value

@henoktsegaye when using withStyles in component you can do

const backgroundColor = props.themedStyle.container.backgroundColor;
Was this page helpful?
0 / 5 - 0 ratings