Getting an <input> value when form is submitted (React)

Getting an <input> value when form is submitted (React)
0

#1

I’m implementing the Wikipedia viewer project as a React exercise. I hit a point where I have to do a state update when I submit the search form.

Here’s a quick pen of the relevant parts: https://codepen.io/graycoatkev/pen/QOYKoV?editors=0010

There’s a main App component with two child components SearchBox and SearchResult. App keeps the search term in its state (since it will pass it down the SearchResult), so I have to make a handler to update it when the form in the SearchBox is submitted.

Now, since the event handler is attached to the <form>, it’s what the event object points to. I figured that a way to derive the search term in the textbox is by using event.target[0].value.

This works, but are there other ways of getting the value from the textbox? I tried event.target.getElementById('search'), but the console says that it’s not a function. I think you can also keep a separate state for SearchBox for the search term, but that means writing more code to update it as the value in the textbox changes (and also, there are gonna be two states for basically the same value, which doesn’t feel right).

Any thoughts?


#2

I know that react discourages directly interacting with the DOM but, when i am dealing with a form input , i always use findDOMNode, remember that you have to reference the input element with refs , see example here: https://github.com/Dereje1/Book-Trading/blob/master/src/components/authentication/login.js


#3

I looked up findDOMNode in the React docs and it said:

In most cases, you can attach a ref to the DOM node and avoid using findDOMNode at all.

So I looked up refs and found how to get the value of the textbox.

handleSearchTermSubmit(event) {
  event.preventDefault();
  this.props.onSearchTermSubmit(this.textInput.value);
}

render() {
    return (
      <div className="SearchBar">
        <h1>SearchBar</h1>
        <form onSubmit={this.handleSearchTermSubmit}>
          <input type="text" ref={(input) => this.textInput = input} />
          <button>Search</button>
          <button type="button">Random (doesn't do anything)</button>
        </form>
      </div>
    );
}

#4

Here’s something you can try:

class Form extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            name: ' ',
            email: ' '
        };
    }

    handleChange = (e) => {
        this.setState({
            [e.target.name]: e.target.value
        })
    }

   onSubmit = (e) => {
       e.preventDefault();
       const form = {
        name: this.state.name,
        email: this.state.email
       }
         {/* -----------you would send data to API to get results, I used database for ease, this also clears the form on submit----------------*/}
       database.push(form);
       this.setState({
            name: '',
            email: ''
       })
    }

    render() {
        return (
            <div>
            <form>
                <label>
                    Name:
                    <input
                        name='name'
                        value={this.state.name}
                        onChange={e => this.handleChange(e)}/>
                </label>
                <label>
                    Email:
                    <input 
                        name='email'
                        value={this.state.email} 
                        onChange={e => this.handleChange(e)}/>
                </label>
                <button onClick={(e) => this.onSubmit(e)}>Send</button>         
            </form>
            </div>
        );
    }
}

export default Form;

#5

I was also thinking about keeping the value of the textbox in the SearchBox component’s state, but it’s already kept by the parent App component’s state (because another component in App needs that value). Managing two states for the same value doesn’t feel right.


#6

You are using a uncontrolled component, therefore, as the documentation suggest it’s probably best to use a ref handler as you implemented.

However you could implement your input as a controlled component: having its value controlled by React instead.

If the value is controlled by a Parent, you can simply pass to the input child component both the value and the methods it has to perform:

class Parent extends Component {
 super(props)
this.state={
  inputVal = ''
}

// define methods
onUpdateInput() {....}
onSubmitForm() {...}

//pass those to the child component
render(){
return (
 <ChildInput 
    value={this.state.inputVal}
    onUpdate={onUpdateInput}
    onSubmit={onSubitForm}
 />
)
}

}

const ChildINput = ({value, onSubmit, onUpdate}) => (
 <input value={value}  onChange={(e) => onUpdate(...)}/>
 <button onClick={() => onSubmit} />
)

So that you have the classic Container / Component structure:

a component who is delegate in how things works, and one on how things looks.

Hope it helps :slight_smile:


#7

Yeah, that is indeed the more ‘reactive’ way to do it, but when I tried to use that on a separate component in the stock charting project I could not make it work, briefly, my main component (Home) calls a dumb component (Addstock) that just displays a form and sends the value of the form back to Home on enter or submit, simple enough right ? not really, at least it wasn’t for me, here are the props and how I called Addstock from Home:

<Addstock
 inputRef={node => this.inputNode = node}
 onKeyDown={this.addingStock}
 buttonSubmit={this.addingStock}
/>

And Addstock has this (amongst other things) as props on FormControl

<FormControl ref={this.props.inputRef}

Now here is the kicker, back in the home component if I use

findDOMNode(this.inputNode).value

I get the value of the input no problem, but , if I try using the ‘reactive’ way

this.inputNode.value

I get undefined for the input value, I have tried many different variations and bindings and could not get it to work without findDOMNode unfortunately

here’s my code base : https://github.com/Dereje1/Stock-Charts/tree/master/src/components