flex vs flexGrow vs flexShrink vs flexBasis in React Native?

Dev01 picture Dev01 · Mar 31, 2017 · Viewed 59.3k times · Source

I finally upgraded react native to 0.42 which includes the introduction of flexGrow, flexShrink, and flexBasis and the change (or fix) of how flex is rendered.

I keep getting errors like:

View was rendered with explicitly set width/height but with a 0 flexBasis. (This might be fixed by changing flex: to flexGrow:) View:

Can someone explain the difference between flex: 1 vs flexGrow: 1. If I apply one or the other to a View it seems to do different things but shouldn't it do the same?

Answer

Dev01 picture Dev01 · Mar 31, 2017

Here's some test code to consider:

render() {
    return <View style={{flex: 1,backgroundColor: "cornflowerblue"}}>
        <View style={{backgroundColor: "chartreuse"}}><Text>Nothing (17px)</Text></View>

        <View style={{flex: 0, backgroundColor: "yellow"}}><Text>flex: 0 (17px)</Text></View>

        <View style={{flex: 0, flexBasis: 10, backgroundColor: "brown"}}><Text>flex: 0, flexBasis: 10 (10px)</Text></View>
        <View style={{flex: 0, flexGrow: 1, backgroundColor: "orange"}}><Text>flex: 0, flexGrow: 1 (97px)</Text></View>
        <View style={{flex: 0, flexShrink: 1, backgroundColor: "tan"}}><Text>flex: 0, flexShrink: 1 (17px)</Text></View>
        <View style={{flex: 0, flexGrow: 1, flexBasis: 10, backgroundColor: "purple"}}><Text>flex: 0, flexGrow: 1, flexBasis: 10 (90px)</Text></View>
        <View style={{flex: 0, flexShrink: 1, flexBasis: 10, backgroundColor: "gray"}}><Text>flex: 0, flexShrink: 1, flexBasis: 10 (10px with 7px hidden below the next element)</Text></View>

        <View style={{flex: 1, backgroundColor: "blue"}}><Text>flex: 1 (80px)</Text></View>

        <View style={{flex: 1, flexBasis: 10, backgroundColor: "cornsilk"}}><Text>flex: 1, flexBasis: 10 (90px)</Text></View>
        <View style={{flex: 1, flexGrow: 1, backgroundColor: "red"}}><Text>flex: 1, flexGrow: 1 (80px)</Text></View>
        <View style={{flex: 1, flexShrink: 1, backgroundColor: "green"}}><Text>flex: 1, flexShrink: 1 (80px)</Text></View>
        <View style={{flex: 1, flexGrow: 1, flexBasis: 10, backgroundColor: "aqua"}}><Text>flex: 1, flexGrow: 1, flexBasis: 10 (90px)</Text></View>
        <View style={{flex: 1, flexShrink: 1, flexBasis: 10, backgroundColor: "pink"}}><Text>flex: 1, flexShrink: 1, flexBasis: 10 (90px)</Text></View>
    </View>;
}

Here's a screenshot of the above code:

Screenshot

Added width and height:

render() {
    return <View style={{flex: 1,backgroundColor: "cornflowerblue"}}>
        <View style={{flex: 0, backgroundColor: "orange"}}><Text>flex: 0 (17px)</Text></View>
        <View style={{flex: 0, width: 700, height: 20, backgroundColor: "yellow"}}><Text>flex: 0, width: 700, height: 20 (20px)</Text></View>

        <View style={{flex: 0, flexBasis: 10, width: 700, height: 20, backgroundColor: "brown"}}><Text>flex: 0, flexBasis: 10, width: 700, height: 20 (10px with 7px hidden below the next element)</Text></View>
        <View style={{flex: 0, flexGrow: 1, width: 700, height: 20, backgroundColor: "orange"}}><Text>flex: 0, flexGrow: 1, width: 700, height: 20 (90px)</Text></View>
        <View style={{flex: 0, flexShrink: 1, width: 700, height: 20, backgroundColor: "tan"}}><Text>flex: 0, flexShrink: 1, width: 700, height: 20 (20px)</Text></View>
        <View style={{flex: 0, flexGrow: 1, flexBasis: 10, width: 700, height: 20, backgroundColor: "purple"}}><Text>flex: 0, flexGrow: 1, flexBasis: 10, width: 700, height: 20 (80px)</Text></View>
        <View style={{flex: 0, flexShrink: 1, flexBasis: 10, width: 700, height: 20, backgroundColor: "gray"}}><Text>flex: 0, flexShrink: 1, flexBasis: 10, width: 700, height: 20 (10px with 7px hidden below the next element)</Text></View>

        <View style={{flex: 1, backgroundColor: "orange"}}><Text>flex: 1 (70px)</Text></View>
        <View style={{flex: 1, width: 700, height: 20, backgroundColor: "blue"}}><Text>flex: 1, width: 700, height: 20 (70px)</Text></View>

        <View style={{flex: 1, flexBasis: 10, width: 700, height: 20, backgroundColor: "cornsilk"}}><Text>flex: 1, flexBasis: 10, width: 700, height: 20 (80px)</Text></View>
        <View style={{flex: 1, flexGrow: 1, width: 700, height: 20, backgroundColor: "red"}}><Text>flex: 1, flexGrow: 1, width: 700, height: 20 (70px)</Text></View>
        <View style={{flex: 1, flexShrink: 1, width: 700, height: 20, backgroundColor: "green"}}><Text>flex: 1, flexShrink: 1, width: 700, height: 20 (70px)</Text></View>
        <View style={{flex: 1, flexGrow: 1, flexBasis: 10, width: 700, height: 20, backgroundColor: "aqua"}}><Text>flex: 1, flexGrow: 1, flexBasis: 10, width: 700, height: 20 (80px)</Text></View>
        <View style={{flex: 1, flexShrink: 1, flexBasis: 10, width: 700, height: 20, backgroundColor: "pink"}}><Text>flex: 1, flexShrink: 1, flexBasis: 10, width: 700, height: 20 (80px)</Text></View>
    </View>;
}

Here's a screenshot of the above code:

Screenshot 2

flex: 0 (default)

  • flex: 0
    • Element takes the size of contents. According to the documentation it should be sized by setting width and height props but it seems to fit to contents if those aren't set.
  • flex: 0, flexBasis: {{px}}
    • Element takes the size given by flexBasis
  • flex: 0, flexGrow: 1
    • With flex: 0 and flexGrow: 1; it's the same as adding the size of the contents (in the example above it's a ) to the size of an element that's set to flex: 1. It's similar to flex: 1, flexBasis: 10 except instead of adding a number of pixels you're adding the size of the content.
  • flex: 0, flexShrink: 1
    • With flex: 0 and flexShrink: 1, the element seems to take the size of the content, in other words it's the same as just flex: 0. I'll bet there are situations where it would be bigger than the content but I haven't see that yet.
  • flex: 0, flexGrow: 1, flexBasis: {{px}}
    • This is the same as flex: 0, flexGrow: 1 except instead of adding the content size to a flex: 1 element it adds the given number of pixels.
  • flex: 0, flexShrink: 1, flexBasis: {{px}}
    • This is the same as flex: 0, flexBasis: {{px}}.
  • flex: 0, height: {{px}}
    • With flex: 0, height is treated just like flexBasis. If there is both a height and flexBasis are set, height is ignored.

flex: 1

  • flex: 1
  • flex: 1, flexBasis: {{px}}
    • With flex: 1 and flexBasis: {{px}}; the value of flexBasis is added to the element's size. In other words, it's like taking a flex: 1 element and adding on the number of pixels set by flexBasis. So if a flex: 1 element is 50px, and you add flexBasis: 20 the element will now be 70px.
  • flex: 1, flexGrow: 1
    • ignored
  • flex: 1, flexShrink: 1
    • ignored
  • flex: 1, flexGrow: 1, flexBasis: {{px}}
    • This is the same as flex: 1, flexBasis: {{px}} since flexGrow is ignored.
  • flex: 1, flexShrink: 1, flexBasis: {{px}}
    • This is the same as flex: 1, flexBasis: {{px}} since flexShrink is ignored.
  • flex: 1, height: {{px}}
    • With flex: 1, height is ignored. Use flexBasis instead.

Here are my observations:

  • Trouble Shooting Tip: Make sure the parent view(s) are giving the children room to grow/shrink. Notice the flex: 1 on the parent view, without it, all the children don't display as you'd expect.
  • Trouble Shooting Tip: Don't use Hot Reloading when testing these values, it can display elements incorrectly after it's reloaded a few times. I recommend enabling Live Reload or using command + r (a lot).
  • The default flex value is flex: 0. If you don't add a flex style value it defaults to 0.
  • Trouble Shooting Tip: if you're tryign to figure out why something isn't displaying like you think it should, start with the (most) parent element and make sure it's giving enough space to it's children to do what they need to do. In other words, try setting it to flex:1 and see if that helps, then go to the next child and repeat.
  • It seems like width is always considered with flexDirection: "column" no matter the other flex props. The same applies for height with flexDirection: "row".
  • After running these test, in general I would use flexBasis over height since flexBasis trumps height.