ReactFix
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • React
  • React Native
  • Programming
  • Object Oriented Programming

Wednesday, December 21, 2022

[FIXED] How can I pass a dynamic string as a key to a style in TS react-native?

 December 21, 2022     parameter-passing, react-native, typescript   

Issue

I want to pass the number of rows (a react native style) dynamically. I tried using 'keyof' as some here have suggested in a different issue, but it didn't work. The code itself works, I'd just like TS to stop screaming at me:

Type '{ backgroundColor: string; flex: number; } | { paddingTop: number | undefined; } | { marginTop: number; fontWeight: "800"; fontSize: number; textAlign: "center"; marginBottom: number; } | { ...; } | ... 7 more ... | { ...; }' is not assignable to type 'StyleProp<ViewStyle>'.

Here's the component:

const Col: React.FC<
  PropsWithChildren<{
    numRows: number;
  }>
> = ({ children, numRows }) => {
  return (
    <View style={styles[`${numRows}col` as keyof typeof styles]}>{children}</View>
  )
}

JSX example:

  <Row>
    <Col numRows={1}>
      <Text>Prompt</Text>
    </Col>
    <Col numRows={3}>
      <Text>This is the prompt</Text>
    </Col>
  </Row>

Here's the relevant style excerpt:

const styles = StyleSheet.create({
//... other styles

  "1col": {
    backgroundColor: "lightblue",
    borderColor: "#fff",
    borderWidth: 1,
    flex: 1
  },
  "2col": {
    backgroundColor: "green",
    borderColor: "#fff",
    borderWidth: 1,
    flex: 2
  },
  "3col": {
    backgroundColor: "orange",
    borderColor: "#fff",
    borderWidth: 1,
    flex: 3
  },
  "4col": {
    backgroundColor: "orange",
    borderColor: "#fff",
    borderWidth: 1,
    flex: 4
  }

});


Solution

The typing is incorrect because you can't accept any number of columns; you should only allow the number of columns you actually support. For example, right now you allow passing 3.14159 to numRows, but don't have a style for that number.

Typescript may be smart enough to check your style names if you limit numRows to only what you do support:

const Col: React.FC<
  PropsWithChildren<{
    numRows: 1 | 2 | 3 | 4;
  }>
> = ({ children, numRows }) => {
  return (
    <View style={styles[`${numRows}col`]}>{children}</View>
  )
}

If not, you can select only the styles you have defined:

const Col: React.FC<
  PropsWithChildren<{
    numRows: 1 | 2 | 3 | 4;
  }>
> = ({ children, numRows }) => {
  const columnStyle = (() => {
    switch (numRows) {
      case 1: return styles['1col'];
      case 2: return styles['2col'];
      // etc
    })();

  return (
    <View style={columnStyle}>{children}</View>
  )
}

However, you may not even need these separate styles, due to a misunderstanding of flex. flex tells the style how much space to take up relative to the other elements in its container. All you need in your case is flex: 1.

If you put any number of elements in your row, all with flex: 1, they will be weighted equally. If you put one element into a row with flex: 4, it will take up the whole row, because the row total is 4 and the element has 4.

You only need different numbers when you want columns of different widths, for example: 1 item with flex: 2 and two items with flex: 1 in a row will give the first item half the space and the other two a quarter each.



Answered By - Abe
Answer Checked By - - Willingham (ReactFix Volunteer)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Google+
  •  Stumble
  •  Digg
Newer Post Older Post Home

Featured Post

Is Learning React Difficult?

React is difficult to learn, no ifs and buts. React isn't inherently difficult to learn. It's a framework that's different from ...

Total Pageviews

Copyright © ReactFix