35

I don't know if I'm doing this correctly... If I want to get value from an input I use this.refs.whatever.value.trim() but if that input is a stateless function component how do I do retrieve the value onSubmit?

I know this isn't correct now after researching but how are you supposed to get value from these input fields?

import React, {Component} from 'react'

import {InputField} from '../components/forms/InputField'
import {Button} from '../components/forms/Button'

export default class SignupWrapper extends Component {
  _handleSubmit(e) {
    e.preventDefault();
    const email = this.refs.email.value.trim();
    const password = this.refs.password.value.trim();
    const confirm = this.refs.confirm.value.trim();
    console.log({email, password, confirm});
  }

  render() {
    return (
      <form id="application-signup" onSubmit={this._handleSubmit.bind(this)}>
        <InputField type={'email'} name={'email'} text={'email'}
                    helpBlock={'email is required'} ref="email" />
        <InputField type={'password'} name={'password'} text={'password'}
                    helpBlock={'password is required'} ref="password" />
        <InputField type={'password'} name={'confirm'} text={'confirm password'}
                    helpBlock={'password confirmation is required'} ref="confirm" />
        <Button type={'submit'} className={'btn btn-primary'} text={'signup'} />
      </form>
    )
  }
}

this is the stateless inputfield

import React from 'react'

export const InputField = (props) => (
  <div className="form-group col-xs-12">
    <label htmlFor={props.name}>{props.text}</label>
    <input type={props.type} name={props.name} className="form-control"
           data-stripe={props.stripe} />
    <span className="help-block">{props.helpBlock}</span>
  </div>
)
cocacrave
  • 2,453
  • 4
  • 18
  • 30
  • http://stackoverflow.com/questions/25941585/react-refs-with-components – jmancherje May 17 '16 at 03:18
  • My advice: avoid refs, always. In your case, make your container stateful, and pass a change handler to the input fields. Whenever input field changes, it calls handler on parent to update state. – wintvelt May 17 '16 at 22:24
  • Thanks, I understand it now. This is a form for stripe, is it safe to store credit card information in state? And why is refs bad? I'm wondering because two tutorials I finished: Official MDG Meteor+React, and LevelUpTuts' Meteor+React for Everyone uses refs. Can you explain it for me? – cocacrave May 19 '16 at 09:24

7 Answers7

57

You can use refs inside stateless components.

Here is also my example fiddle that shows you how it works.

import React from 'react'

export default ({ onChange }) => {
  let cityInput

  const onSubmit = e => {
    e.preventDefault()
    onChange(cityInput.value)
  }

  return (
    <form onSubmit={ onSubmit }>
      <input type='text' placeholder='Enter City Name'
        ref={ el => cityInput = el } />
      <button>Go!</button>
    </form>
  )
}

Note: Although this is one way to do it, however, this approach is not recommended unless you really need it. Try to think about more on redesigning your React code instead of hacking it like this. You may read more about it here.

Inanc Gumus
  • 25,195
  • 9
  • 85
  • 101
  • are you sure mate? – Muhaimin Dec 13 '16 at 12:44
  • @MuhaiminAbdul Yes, I'm sure. Look at my fiddle: https://jsfiddle.net/inancgumus/dvfdj4wx/ – Inanc Gumus Dec 14 '16 at 10:16
  • forgot to pay attention on the `let` cityInput – Muhaimin Dec 15 '16 at 02:33
  • inainc, if I use ref like this on state change (the state comes as a property from the parent), it seems like ref is undefined, if I `setTimeout(foo, 0)`, it works. Any idea on how to solve this? – Himmators Jan 13 '17 at 09:56
  • @Himmators Can you send an example fiddle? – Inanc Gumus Jan 13 '17 at 10:13
  • 6
    This should be the accepted answer. May not be the optimum setup, but for stateless functional controls where you want access to the rendered DOM node, this is as good as it gets as of Jan 2017. – mwigdahl Jan 19 '17 at 15:49
  • https://codepen.io/DZuz14/pen/NyjYQL?editors=0011 shows the approach recommended from the React Docs. They explicitly say they do not recommend that you use refs in most situations anyways, as it could potentially lead to breaking code later as React gets updated. – Dan Zuzevich Feb 12 '18 at 02:18
  • 1
    I disagree strongly with the comment that has 6 upvotes. This is not the best approach as of Jan 2017. It is much safer to store your input fields in a parent components state. Please read the official documentation for handling forms. – Dan Zuzevich Feb 12 '18 at 02:20
  • 2
    @DanielZuzevich Downvoting a correct answer is a disservice to this community. The question was about how to do it with refs, and this the correct answer. It wasn't asking about the recommended way. We're already aware of that. – Inanc Gumus Feb 12 '18 at 12:47
  • Change your answer to say dont use refs and do it the other way then. That's more beneficial for the community. – Dan Zuzevich Feb 12 '18 at 16:16
  • It's not recommended to create handlers in render, as it breaks `shouldComponentUpdate` and can trigger GC. – Markus Apr 17 '18 at 16:09
  • That's right. It's not recommended. But, this is a way to do it. If you've an answer please provide. – Inanc Gumus Apr 29 '18 at 09:18
  • How do I test this? How can I access cityInput in test using enzyme/jest? – newbie May 29 '18 at 11:33
  • Don't test what cityInput contains, test the behavior of the component. Fill the values programmatically to input and click, then check. – Inanc Gumus May 29 '18 at 14:33
18

as of react 16.8 you can use the useRef hook, from the docs:

useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}
cubefox
  • 1,251
  • 14
  • 29
  • Cannot make it work in `react 16.8.6`. It's saying `Uncaught TypeError: textFieldRef.current.focus is not a function`. – Maris Apr 12 '19 at 15:18
  • @Maris you have to reload the page for refs to become active and **useRef has to be imported** with react. – cubefox Apr 14 '19 at 18:04
16

Edit: Looks like this is not the issue anymore, since new ideas on how to handle this situation arose since this answer was written. Refer to inanc's answer instead of this one.

Refs are unavailable in stateless components. From React Docs:

Because stateless functions don't have a backing instance, you can't attach a ref to a stateless function component. Normally this isn't an issue, since stateless functions do not provide an imperative API. Without an imperative API, there isn't much you could do with an instance anyway. However, if a user wants to find the DOM node of a stateless function component, they must wrap the component in a stateful component (eg. ES6 class component) and attach the ref to the stateful wrapper component.

Moti Azu
  • 5,392
  • 1
  • 23
  • 32
  • 1
    Actually you can reference a dom element from a stateless component. From the documentation: **[You can, however, use the ref attribute inside a functional component as long as you refer to a DOM element or a class component](https://reactjs.org/docs/refs-and-the-dom.html#exposing-dom-refs-to-parent-components)** – Sadok Mtir Jan 19 '18 at 11:02
4

@inanc, show good method, but I propose an alternative way to use event target to get the DOM element reference. As you are using the form element, you can name your input elements and use it to access the form object.

const onSubmit = fn => e => {
  e.preventDefault()
  const city = e.target.city.value // Access elements through `form`
  if (city) {
    fn(city)
  }
}

const MyComponent = ({
  onChange
}) => {
  return ( 
    <div>
      <form onSubmit={onSubmit(onChange)}>
        <input type='text' name='city' placeholder='Enter City Name' />
        <button>Go!</button>
      </form>
    </div>
  )
}
Risto Novik
  • 8,199
  • 9
  • 50
  • 66
2

Nowadays, you would want to avoid to use the ref attribute to get values from input fields. Instead you should use React's local state. The ref attribute is reserved for only a few use cases:

  • Managing focus, text selection, or media playback
  • Integrating with third-party DOM libraries
  • Triggering imperative animations
Robin Wieruch
  • 14,900
  • 10
  • 82
  • 107
2

You can't use refs in stateless react components (+1 to Moti Azu for his snippet from the documentation).

You can use multiple techniques to get stuff in/out of stateless components (without using Ref or use class components), I have created the snippet below to illustrate

  1. How you can pass stuff into a stateless component (the label props set statically in the parent component).
  2. How you can get stuff from a stateless component without use of refs (the input component)

try it out & let me know if you still have a problem, enjoy...

// Stateless Component (just a <div> component with prop)
const StatelessComponent = props => (
  <div>{props.label}</div>
);

// Stateless input Component
const InputComponent = props => {
  return (
    <input 
      value={props.name} 
      onChange={props.handleChange} 
    />
  );
};

// Parent Class Component
class ParentComponent extends React.Component {
  state = {
    firstName: "HELCODE"    
  };

  handleChange = event => {
    this.setState({
      firstName: event.target.value,
    });
  };
  
  render() {
    const {title} = this.props;
    console.log("rendered");
    return (
      <div>
        <h3>{title}</h3>
        <StatelessComponent 
          label="This is a label passed to a stateless component as prop" 
        />
        <br/>
        <InputComponent
          name={this.state.firstName}
          handleChange={this.handleChange}
        />
        
        <p>{this.state.firstName}{this.state.lastName} can read stuff from stateless components withough Ref using States</p>
        <br/>
        
      </div>
    );
  }
}

// Render it
ReactDOM.render(
  <ParentComponent title="Parent Component" />,
  document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
helcode
  • 1,859
  • 1
  • 13
  • 32
  • Your answer is misleading. Maybe because it's outdated, but in any case, you CAN use refs in stateless components. You can read about it in the ReactJS docs, at the very bottom of the Accessing Refs section: https://reactjs.org/docs/refs-and-the-dom.html#accessing-refs – p-syche Feb 05 '20 at 10:56
  • Are you referring to `forwardRef` workaround, or something else? – helcode Feb 08 '20 at 21:52
-1
import React, { useEffect, useRef } from 'react';

function TextFocusTest () {
  const textRef = useRef(null);

  useEffect(() => {
     textRef.current.focus();
  });

  return(
    <input ref{textRef} type="text">
  );

} 

export default TextFocusTest;
Gabriel Scalici
  • 545
  • 5
  • 4
  • 1
    Please don't post only code as an answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are generally of higher quality, and are more likely to attract upvotes. – Mark Rotteveel Mar 07 '20 at 08:30