1

So I was trying to make this component have the styles assigned dynamically by having the style chosen from the stylesheet depend on the string passed in through the properties

export type MyComponentProps = {
  styleName: string;
}

const MyComponent = (props: MyComponentProps) => {
  const { styleName } = props;
  
  return (
    <View
      style={[
        styles['container'],
        styles[`view${styleName}`],
      ]}>
      <Text
        style={[
          styles['text'],
          styles[`text${styleName}`],
        ]}>
        Hello World
      </Text>
    </View>
  );
}


const styles = StyleSheet.create({
  container: {
    width: '100%',
    alignItems: 'center',
  },

  viewDark: {
    backgroundColor: 'black',
  },

  viewLight: {
    borderColor: 'white',
  },

  text: {
    fontWeight: 'bold',
  },

  textDark: {
    color: 'white',
  },

  textLight: {
    color: 'black',
  },
}

But this raises the error

Element implicitly has an 'any' type because expression of type '`view${any}`' can't be used to index type '{ container: { width: string; alignItems: "center"; }; viewDark: { backgroundColor: string; }; viewLight: { backgroundColor: string; }; text: { ...; }; textDark: { ...; }; textLight: { ...; }; }'.

I was trying to avoid having any styles themselves come in through the props, I see that's something recommended a lot

Also I know it might hurt your eyes to see styles['container'] instead of styles.container, but I just wanted to make it clear what my goal is as much as possible, so having the ${styleName} be the only difference between the two cases would help that

2 Answers 2

1

So after some research we can easily do this as long as we cast that dynamically created string to a keyof whatever interface we attribute to the styles variable. So in this case this could be accomplished with

export type MyComponentProps = {
  styleName: string;
}

const MyComponent = (props: MyComponentProps) => {
  const { styleName } = props;
  
  return (
    <View
      style={[
        styles['container'],
        styles[`view${styleName}` as keyof IStyles],
      ]}>
      <Text
        style={[
          styles['text'],
          styles[`text${styleName}` as keyof IStyles],
        ]}>
        Hello World
      </Text>
    </View>
  );
}

interface IStyles {
  container: object,
  viewDark: object,
  viewLight: object,
  text: object,
  textDark: object,
  textLight: object,
}

const styles: IStyles = StyleSheet.create({
  container: {
    width: '100%',
    alignItems: 'center',
  },

  viewDark: {
    backgroundColor: 'black',
  },

  viewLight: {
    borderColor: 'white',
  },

  text: {
    fontWeight: 'bold',
  },

  textDark: {
    color: 'white',
  },

  textLight: {
    color: 'black',
  },
}

Don't forget to declare styles as having the interface IStyles with const styles: IStyles = ...

Sign up to request clarification or add additional context in comments.

Comments

0

So there are some occasions where the other solution seems to not work, but I'll still leave it so readers can be aware of any option. This one hasn't failed in any case, and we have no need to create an interface, so that may be a plus as well

export type MyComponentProps = {
  styleName: string;
}

const MyComponent = (props: MyComponentProps) => {
  const { styleName } = props;

  const viewStyle = `view${styleName}`; // Possibly more readable
  
  return (
    <View
      style={[
        styles['container'],
        `view${styleName}` == 'viewDark'
          ? styles['viewDark']
          : {},
        viewStyle == 'viewLight' // Using the more readable alternative
          ? styles['viewLight']
          : {},
      ]}>
      <Text
        style={[
          styles['text'],
          `text${styleName}` == 'textDark'
          ? styles['textDark']
          : {},
          `text${styleName}` == 'textLight'
          ? styles['textLight']
          : {},
        ]}>
        Hello World
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    width: '100%',
    alignItems: 'center',
  },

  viewDark: {
    backgroundColor: 'black',
  },

  viewLight: {
    borderColor: 'white',
  },

  text: {
    fontWeight: 'bold',
  },

  textDark: {
    color: 'white',
  },

  textLight: {
    color: 'black',
  },
}

With the downside that we have to write the if statement manually to check if it equals each of the possible styles separately, but the upside that it appears to be more reliable

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.