2

I'm following this Udemy react-native course and in one of the examples he uses a picker to select data in the screen. Back when he tried it it seems like it was working but now I get a weird result when I try to render it.

If I follow his code exactly the picker shows after all the other items, after making some changes I get it to show kind of at the right place but it is now squished, which is still not correct:

bad pickler

I am definitely doing something wrong here in terms of how to render it, here's the code (full example on github):

import React from 'react';
import {Picker, Text, StyleSheet, View} from 'react-native';
import {connect} from 'react-redux';
import {Card, CardSection, Input, Button} from "./common";
import {employeeUpdate} from "../actions";

class EmployeeCreate extends React.Component {

  updateEmployee(name, value) {
    this.props.employeeUpdate({prop: name, value: value})
  }

  renderPickerItems() {
    return ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
      .map((item) => <Picker.Item key={item} label={item} value={item}/>);
  }

  render() {
    return (
      <Card>

        <CardSection>
          <Input
            label="Name"
            placeholder="Your Name"
            value={this.props.name}
            onChangeText={this.updateEmployee.bind(this, 'name')}
          />
        </CardSection>

        <CardSection>
          <Input
            label="Phone"
            placeholder="555-555-5555"
            keyboardType="phone-pad"
            value={this.props.phone}
            onChangeText={this.updateEmployee.bind(this, 'phone')}
          />
        </CardSection>

        <CardSection style={{flex: 1}}>
          <View style={styles.shiftContainerStyle}>
            <Text style={styles.pickerTextStyle}>Shift</Text>
            <Picker
              style={styles.pickerStyle}
              selectedValue={this.props.shift}
              onValueChange={this.updateEmployee.bind(this, 'shift')}
            >
              {this.renderPickerItems()}
            </Picker>
          </View>
        </CardSection>

        <CardSection>
          <Button>
            Create
          </Button>
        </CardSection>

      </Card>
    );
  }

}

const styles = StyleSheet.create({
  pickerTextStyle: {
    fontSize: 18,
    lineHeight: 23,
    flex: 1,
  },
  pickerStyle: {
    flex: 2,
  },
  shiftContainerStyle: {
    flex: 1,
    flexDirection: 'row',
    alignItems: 'center',
  }
});

const mapStateToProps = state => {
  const {name, phone, shift} = state.employeeForm;

  return {
    name,
    phone,
    shift,
  };
};

export default connect(mapStateToProps, {employeeUpdate})(EmployeeCreate);

Any idea what I could do to render this correctly?

Maurício Linhares
  • 39,901
  • 14
  • 121
  • 158
  • What is it supposed to look like? If you remove the `style={{flex: 1}}` from that third `CardSection`, it'll render as I would expect it to render. But without access to the course, I have nothing to compare it against. – Michael Cheng Mar 08 '18 at 02:49
  • I'd expect to render just like the inputs, by the right of the shift label with the button below them as a block. – Maurício Linhares Mar 08 '18 at 04:19

1 Answers1

5

You need to remove style={{flex: 1}} from this line in your code:

<CardSection style={{flex: 1}}>

The reason being that your parent container, Card, doesn't have any flex or width/height values defined. If flex is left undefined, the default is flex: 0. If you look at the docs for flex, you'll see that:

When flex is 0, the component is sized according to width and height and it is inflexible.

Combine that with having no width/height defined and you get this behavior on rendering your CardSections:

  • The three CardSections (input, input, button) will take up the default width and height based on their children. That is the default styling for the Inputs and Button.
  • The CardSection with style={{flex: 1}} will calculate its width and height based on the remaining space taken up by the parent container(s) per the definition of flex: 1:

When flex is a positive number, it makes the component flexible and it will be sized proportional to its flex value. So a component with flex set to 2 will take twice the space as a component with flex set to 1.

  • The parent container, Card, in this case has no extra space left. So what happens is that this CardSection ends up with 0 height. Hence the strange overflow rendering you're seeing.

Once you remove style={{flex: 1}}, the width and height of the CardSection will be defined by it's child components which, like Input and Button, do have a styles and default styles.

Whether or not this is correct behavior per the Yoga spec (Yoga is what React Native uses for layout) is up for debate and has tripped up others before. Definitely look over that first StackOverflow answer I linked to as it has far more detail and explanation on gotchas than any of the documentation on React Native wrt flex.

Michael Cheng
  • 9,644
  • 6
  • 46
  • 48