1

I'm creating a simple event tracker app using React-Native, Redux, React-Navigation and Expo and I'm having a bit of trouble with a fairly trivial part.

I have a form screen where you enter the event name, date and other information. I'm using <TextInput/> to enter the event's name but it won't to the onChange event, only onChangeText. Additionally when I use onChange event, the Back button on the navigation becomes unresponsive and the cursor remains only in the TextInput no matter where I click

I'm having another problem related to memory which causes the simulator to load longer and longer after each successive build, and become unresponsive after a few loads. My mobile expo app also crashes occasionally. I have a somewhat vague feeling that these 2 issues are related but I'm totally lost as to what the issue may be. Basically the Exponent simulator doubles in memory usage every time i save a file.

Here is the the CreateEventScreen.js looks like at the moment, I'm going to convert it to Redux as soon as I resolve the onChange issue.

export default class CreateEvent extends Component {
  constructor(){
    super();
    this.state = {
      name: '',
      date: new Date(),
      repeatAnnually: false,
      showDatePicker: false,
      wallPaperSource: null
    };
    this.toggleSelectWallPaper = this._toggleSelectWallPaper.bind(this);
    this.handleChange=this._handleChange.bind(this);
  }

  static navigationOptions = {
    title: 'Create New Event'
  }

  _handleToggleSwitch = () => this.setState(state => ({
    repeatAnnually: !this.state.repeatAnnually
  }));

  /* THIS IS NOT WORKING */
  _handleChange = (event) => {
    console.log(event);
    const target = event.target;
    const value = target.value;
    const name = target.name;
    this.setState({
      [name]: value
    })
  }

  _onDateChange = (date) => this.setState(state => ({
    date: date
  }));

  _toggleSelectWallPaper = async() => {
    let result;
    try {
      result = await ImagePicker.launchImageLibraryAsync({
        allowEditing: true,
        aspect: [4, 3]
      });
    } catch (e) {
        result = null;
    }

    if(!result.cancelled){
      this.setState({
        wallPaperSource: result.uri
      });
    }

  }

  _renderDatePicker(){
    if(this.props.showDatePicker){
      return (
          <DatePickerIOS
            date={this.state.date}
            mode="date"
            onDateChange={this._onDateChange}
          />
      )
    } else {
      return null;
    }
  }

  render(){
    const { onClick, toggleDatePicker, showDatePicker } = this.props
    let { wallPaperSource } = this.state;
    return(
      <View>
        <TextInput
          style={styles.input}
          name="name"
          placeholder="Event Name"
          value={this.state.name}
          /* THIS IS NOT WORKING */
          onChange={this._handleChange}
          /* This works but not ideal for redux integration */
          // onChange={(event) => this.setState({'name': event.target.value})}  
        />
        <View style={styles.eventDetailsContainer}>
          <View style={styles.itemContainer}>
            <Text>Repeat Annually</Text>
            <Switch
              onValueChange={this._handleToggleSwitch}
              value={this.state.repeatAnnually}
            />
          </View>
          <TouchableOpacity onPress={e => {
            e.preventDefault()
            toggleDatePicker(showDatePicker)
          }}>
            <View style={styles.itemContainer}>
              <Text>Select Date</Text>
              <Text>{this.state.date.toLocaleDateString()}</Text>
            </View>
          </TouchableOpacity>
          <View>
            {this._renderDatePicker()}
          </View>
        </View>
        <View style={styles.eventDetailsContainer}>
          <TouchableOpacity onPress={this.toggleSelectWallPaper}>
            <View style={styles.itemContainer}>
              <Text>Wallpaper</Text>
              { this.state.wallPaperSource  ?
                <Image source={{ uri: wallPaperSource }} style={{ width: 100, height: 100 }}/>
                 : null
              }
            </View>
          </TouchableOpacity>
        </View>
        <Button
          style={styles.submitBtn}
          onPress={e => {
            e.preventDefault()
            if(!this.state.name.trim()){
              return
            }
            onClick(this.state)
          }}
          color="#1e90ff"
          title="Create Event"
        />
    </View>
    )
  }
}

package.json -

{
  "name": "floss-evens",
  "version": "0.0.0",
  "description": "Hello Expo!",
  "author": null,
  "private": true,
  "main": "node_modules/expo/AppEntry.js",
  "dependencies": {
    "expo": "^18.0.4",
    "lodash": "^4.17.4",
    "moment": "^2.18.1",
    "prop-types": "^15.5.10",
    "react": "16.0.0-alpha.12",
    "react-native": "https://github.com/expo/react-native/archive/sdk-18.0.1.tar.gz",
    "react-native-image-picker": "^0.26.3",
    "react-navigation": "^1.0.0-beta.11",
    "react-redux": "^5.0.5",
    "redux": "^3.7.2"
  }
}

Any help or feedback would be greatly appreciated. Thanks!

yoleg
  • 371
  • 3
  • 12
  • Is there a reason you don't wish to use `onChangeText`? Just a guess, but the event object might be huge, and simply logging it might halt other js execution. If you remove `console.log`, does that resolve the unresponsiveness problem? – NiFi Aug 10 '17 at 06:04
  • My rational behind not using `OnChangeText` is that it's not very reusable in the sense that I would have to manually set the key that's being updated. Additionally, i think it would get more complicated when I implement redux into it this component. You were correct about removing the `console.log`! Thank you for that. The `onChange` event works. However now i'm logging the target `console.log('target', event.target)` and all it's giving me is "47" for any input. – yoleg Aug 10 '17 at 06:27
  • All right. That's also possible to solve by supplying additional parameters to `onChangeText`: `onChangeText={(value) => this._handleChange(value, 'keyName')}`. A redux integration should not change this logic, only make the information flow chain longer. – NiFi Aug 10 '17 at 06:36
  • Yeah I figured i'd be able to do it with `onChangeText` but I'd prefer to do it dynamically, rather than passing in a static value. Thanks for your help. – yoleg Aug 10 '17 at 14:45

1 Answers1

1

The event object supplied to onChange is large, and will halt other js execution if logged. Simply remove console.log(event);.

When using onChange, the input value can be retrieved using event.nativeEvent.text. This is not how you're supposed to capture input values, though. The correct approach is to use onChangeText.

TextInput does not have a property called name, that will simply be ignored.

// This will not be defined
const name = target.name;

You can supply additional parameters to _handleChange to accomplish the intended action:

onChangeText={(value) => this._handleChange(value, 'keyName')}

If you want to handle this automatically e.g. for a large number of inputs, see this question.

NiFi
  • 2,398
  • 1
  • 17
  • 26