1

I've got a <TextInput> and I gave it a ref attribute by doing this:

ref={node => this.usernameInput = node;}

Then, I have a method when the user presses a <TouchableInput> that tries to get the value of this.usernameInput. this.usernameInput is not null in my onPress method, but I can't find a way to get the value of it! When I do this:

console.log(this.usernameInput.value);

It logs undefined. If I set a breakpoint, and inspect this.usernameInput I can see it, but there is no value property or method, and I don't see any property or method that could return the current value. How do I get the value of my <TextInput>?

EDIT

Here's my component class:

import {
  View,
  Text,
  TextInput,
  TouchableHighlight
} from 'react-native';
import {Manager as ModalManager} from 'react-native-root-modal';

class AppContainer extends React.Component {
  loginModal;

  constructor(props){
      super(props);
    this._onLoginPress = this._onLoginPress.bind(this);
      this._createLoginModal = this._createLoginModal.bind(this);
  }

  async componentWillMount() {
    this._createLoginModal();
  }

  render() {
      return (
        <View >
          <Text>Hello.</Text>
        </View>
      );
    } 
  }

  _onLoginPress() {
    //this returns 'undefined', although this.usernameInput is not undefined
    console.log(`USERNAMEinputValue: ${this.usernameInput.props.value}`);
  }

  _createLoginModal() {
    this.loginModal = new ModalManager(
        <View >
          <View >

            <Text>Username:</Text>
            <TextInput
              placeholder="Username"
                      ref={node => {
                        this.usernameInput = node;
              }}
            ></TextInput>

            <TouchableHighlight
              onPress={this._onLoginPress}
            >
              <Text>Login</Text>

            </TouchableHighlight>

          </View>
        </View>
    );
  }

}
Tyler Jones
  • 1,202
  • 4
  • 17
  • 36

3 Answers3

3

I found a parameter _lastNativeText which holds the text in TextInput component after going through the react native source code. You may use that param to get the value like below for your example.

this.usernameInput._lastNativeText

But still I would not suggest using this approach. Better to use state based approach suggested by @jayce444 and @nifi

Community
  • 1
  • 1
Jickson
  • 4,643
  • 1
  • 23
  • 35
  • yes, this works. however, why is there an underscore at the start of the name? Is this method unstable? – Tyler Jones Oct 31 '16 at 04:48
  • It means private fields or private methods. Methods that are only for internal use. Check http://stackoverflow.com/questions/8288756/in-javascript-what-does-this-underscore-mean for more info. This is the reason why its not advisable to use _lastNativeText field to get the text. – Jickson Oct 31 '16 at 06:08
1

What about storing the username in state?

<TextInput
  ref="username" 
  onChangeText={(username) => this.setState({username})}
  value={this.state.username} 
/>

Then in _onLoginPress:

console.log(this.state.username);
Jayce444
  • 7,265
  • 3
  • 21
  • 34
  • 1
    yes, I can do that, but that would require making a little state inside of each component, when I want to use Redux to manage my state. I'm trying to understand why I can't simply get the text out of a TextInput that I'm storing a reference to? – Tyler Jones Oct 30 '16 at 02:24
1

Using a ref in React (and RN) is generally an anti-pattern. See Don't Overuse Refs and Lifting State Up. In some cases it is necessary, but this is not one of those cases. There are at least three viable options since you are using Redux.

Not in preferential order:

  1. Using local state in the component:

    constructor(props) {
      super(props);
    
      this.state = {
        username: ''
      };
    }
    
    render() {
      return (
        <TextInput
          placeholder="Username"
          value={this.state.username}
          onChangeText={(username) => this.setState({username})}
        />
      );
    }
    
  2. Storing the value in the global state and using e.g. the react-redux middleware's connect function to map that part of the state to the component's props. For instructions how to implement this, see using react-redux with Redux.

  3. Storing the value in a parent component. The idea for this is the same as in the first code example, except that you would provide the value and onChange callback function as props to the child component.

In all of these cases you can output the value of the input by using

console.log(this.state.username); // (1) & (3)

or

console.log(this.props.username); // (2)


EDIT: Reasoning

Which option to choose depends on the situation, and is sometimes purely a matter of preference. See this comment from the author of Redux. tl;dr do what seems most logical/reasonable. This often means "lifting up" the state, or using the global state. As a rule of thumb it's possible to reason, that whenever it is solely the component at hand that needs the data, store it in local state.

A counterargument would be that stateless functional components are to be preferred. See reasoning e.g. here. The react docs used to mention possible future performance benefits for functional components, but that has since been removed. Whenever you don't necessarily need the component lifecycle methods or a local state, use stateless functional components and store state higher up in the component chain.

NiFi
  • 1,928
  • 1
  • 11
  • 23
  • 1
    Thanks for this info. However, could you explain how it's not an anti-pattern, nor breaking the Lifting State Up principle, to create little states all over the app, just for temporarily storing text input, before I feed it to my redux container? Since all i need is the value of the textinput, it seems like the "right" way to do it, would be to only capture the textinput when it's ready to feed to my redux state container, instead of having many little states everywhere. – Tyler Jones Nov 01 '16 at 05:27