1

I have read similar questions here

Why shouldn't JSX props use arrow functions or bind?

JSX props should not use .bind() - how to avoid using bind?

No .bind() or Arrow Functions in JSX Props making no sense to me when I need to pass arguments

And I understand how using an arrow function causes the function to be recreated on each render and effects performance. However I still don't fully understand how to resolve this issue in React Native, specifically when using setState.

For example, if I have a TextInput that updates a value held in the component state in the onChangeText function, how do I avoid using an arrow function?

<TextInput
  value={this.state.text}
  onChangeText={text => this.setState({ text })}
  />

Must I create a handler for each property in the state that I want to update? For example, if I have two TextInput fields Email and Password, would that need to be handled like this?

  updateEmail = email => {
    this.setState({ email })
  }

  updatePassword = password => {
    this.setState({ password })
  }

  render() {
  ...
  <TextInput
      value={this.state.email}
      onChangeText={this.updateEmail}
      />
  <TextInput
      value={this.state.password}
      onChangeText={this.updatePassword}
      />
wizloc
  • 2,202
  • 4
  • 28
  • 54

2 Answers2

4

You can set id or name of an element same with the name of a state variable. So you can create one function and pass it to all inputs like:

onFieldChange = (e) => {
    const key = e.target.id
    const value = e.target.value
    this.setState({[key]:value})
}
Ali Ankarali
  • 2,761
  • 3
  • 18
  • 30
  • I tried setting id and name on TextInput like `TextInput id="customer" name="customer" onChange={this.onFieldChange} />` but onFieldChange e.target.id and e.target.value are undefined. How are you accomplishing this? – wizloc Apr 27 '18 at 16:14
  • Did you bind your function? More info on that here: https://medium.freecodecamp.org/react-binding-patterns-5-approaches-for-handling-this-92c651b5af56 – Ali Ankarali Apr 27 '18 at 17:24
  • You don't have to bind when using an arrow function I thought? – wizloc Apr 27 '18 at 17:26
  • That's binding as well. I just asked to check. So your e.target is completely undefined? – Ali Ankarali Apr 27 '18 at 17:31
  • Correct. I can call e.nativeEvent, but that does not contain id or name when I set them on the element. – wizloc Apr 27 '18 at 17:33
  • Can you pass name as parameter like onFieldChange = (name, e) => ? – Ali Ankarali Apr 27 '18 at 17:34
  • I just found this function in a forum and it seems to be working but it is not similar to your solution or others I have found on SO `handleTextChange = fieldName => { return event => { this.setState({ [fieldName]: event.nativeEvent.text }) } }` – wizloc Apr 27 '18 at 17:35
  • The above function works so I will use it, although I am confused why I cannot use your method because when searching SO I have found others using your method also – wizloc Apr 27 '18 at 17:36
  • If that worked what I wrote just above should have also worked. Interesting. Try to mess up with the code maybe you'll figure it out. – Ali Ankarali Apr 27 '18 at 17:41
  • Ah I found that example as well. There you send field name explicitly as a parameter. – Ali Ankarali Apr 27 '18 at 17:46
0

Generic way

Create your own TextInput component that takes an id prop and passes it as a second parameter when calling the handler. This is the generic way to handle it:

class MyTextInput extends Component {

    handleTextChange = text => {
        const {id, onTextChange} = this.props;
        return onTextChange && onTextChange(text, id);
    }

    render() {
        return (
            <TextInput {...this.props} onTextChange={this.handleTextChange}
        );
    }
}

And use it like that:

class App extends Component {

    handleTextChange = (text, id) => this.setState({[id]: text});

    render() {
        const {email, password} = this.state;

        return (
            <div>
                <MyTextInput
                    id="email"
                    value={email} 
                    onTextChange={this.handleTextChange}
                />
                <MyTextInput
                    id="password" 
                    value={password}
                    onTextChange={this.handleTextChange}
            </div>
        );
    }
}

As it does not use arrow functions it avoids performance penalties if you have a large amount of components. But note that the penalty is marginal for only a few components and not worth the effort in most of the cases. Just make sure you do not forward the handler that is an arrow function multiple levels deep.

Easy way for react-native TextInput

Use the onChange handler which gets the event instead of only the text. The event also contains the name of the input if one way provied to it:

class App extends Component {

    handleChange = event => {
        const {name, value} = event.nativeEvent;
        this.setState({[name]: value});
    }

    render() {
        const {email, password} = this.state;

        return (
            <div>
                <MyTextInput
                    name="email"
                    value={email} 
                    onChange={this.handleChange}
                />
                <MyTextInput
                    name="password" 
                    value={password}
                    onChange={this.handleChange}
            </div>
        );
    }
}
trixn
  • 15,761
  • 2
  • 38
  • 55
  • I am trying this method (the easy way for react-native). I have onChange in my TextInput being handled by a method like your example. But I can't get name or value from event.nativeEvent. I only see `{ eventCount: 1, target: 277, text: (whatever I have typed) }`. Any ideas? – wizloc Apr 27 '18 at 13:20