How can I get an input's value on a button click in a Stateless React Component?

Viewed 23704

I have the following functional component

const input = props => (
  <div>
    <input placeholder="Type a message..." />
    <div onClick={props.send} className="icon">
      <i className="fa fa-play" />
    </div>
  </div>
)

How could I possibly pass the value of the input to props.send()?

4 Answers

I found a solution for this exact scenario on React's official docs: https://reactjs.org/docs/refs-and-the-dom.html#refs-and-functional-components

This approach allows your component to remain stateless and also doesn't require you to update the parent component on every change.

Basically,

const input = props => {

let textInput = React.createRef();

function handleClick() {
  console.log(textInput.current.value);
}

return (
    <div>
      <input ref={textInput} placeholder="Type a message..." />
      <div onClick={handleClick} className="icon">
        <i className="fa fa-play" />
      </div>
    </div>
  )
}

Edit May 2021: Since this answer seems to be getting some attention, I have updated the answer to use a hooks based approach as well, since that is what I would use now (If using React 16.8 and above).

const input = props => {  
  const [textInput, setTextInput] = React.useState('');

  const handleClick = () => {
    console.log(textInput);
    props.send(textInput);
  }

  const handleChange = (event) => {
    setTextInput(event.target.value);
  }

  return (
    <div>
      <input onChange={handleChange} placeholder="Type a message..." />
      <div onClick={handleClick} className="icon">
        <i className="fa fa-play" />
      </div>
    </div>
  )
}

There are many ways to do it since you're very much concerned about performance. Here is the implementation, your component will be rendered only when you click on send button which actually means state will be updated once and input value will be displayed in parent component.

const Input = props => {
  return (
    <div>
      <input onChange={props.changeHandler} placeholder="Type a message..." />
      <button onClick={props.send}>send</button>
    </div>
  );
};

class App extends Component {
  state = {
    inputValue: ""
  };

  inputValue = '';

  send = () => {
    this.setState({ inputValue: this.inputValue });
  };

  changeHandler = event => {
    this.inputValue = event.target.value;
  };

  render() {
    console.log("In render");
    return (
      <React.Fragment>
        <Input changeHandler={this.changeHandler} send={this.send} />
        <div> {this.state.inputValue}</div>
      </React.Fragment>
    );
  }
}

Since you mentioned that you just started with React, I'd suggest that you work through the documentation (which offers nice explanation).

According to your comment, the usage of a functional component is not a requirement. Therefore I'd recommend to do it that way -->

Your CustomInput component:

import React from "react";
import PropTypes from "prop-types";

class CustomInput extends React.Component {
  constructor() {
    super();
    this.textInput = React.createRef();
  }

  static propTypes = {
    send: PropTypes.func
  };

  render() {
    const { send } = this.props;
    return (
      <React.Fragment> 
        <input placeholder="Type a message..." ref={this.textInput} />
        <div
          onClick={() => send(this.textInput.current.value)}
          className="icon"
        >
          CLICK ME
        </div>
      </React.Fragment>
    );
  }
}

export default CustomInput;

If you noticed, I've replaced the empty div with React.Fragment. In that case you can omit the unnecessary <div> wrappings (if those are not required) which will keep your DOM clean (Read more about it here.

Usage:

<CustomInput
   send={(prop) => {
      console.log(prop)
   }}
/>

I just used a dummy function which will log the input value to the console..

You can check the working example (Make sure to trigger the console in the editor) here

Posting this answer, If incase someone is using an earlier release of React 16.3. We can achieve the same thing by using callback refs instead without having to maintain state or having onChange event handler:

const input = props => (
  <div>
    <input ref={props.myRef} placeholder="Type a message..." />
    <div onClick={props.send} className="icon">
      <i className="fa fa-play" />
    </div>
  </div>
)

Calling that Input Component

handleClick = () => console.log(this.inputRef.value);

<Input myRef={el => this.inputRef = el} send={this.handleClick} />
Related