React无状态组件中的this.refs..value是什么?

35

我不知道我是否做得正确... 如果我想从一个输入框中获取值,我会使用 this.refs.whatever.value.trim(),但是如果这个输入框是一个无状态函数组件,那么在提交时该怎么办呢?

我知道现在这种方法不正确,但是你应该如何从这些输入字段中获取值呢?

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>
    )
  }
}

这是一个无状态的输入框

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>
)

https://dev59.com/q18e5IYBdhLWcg3wPYdA - jmancherje
我的建议是:尽量避免使用 refs。在你的情况下,将容器设为有状态的,并将更改处理程序传递给输入字段。每当输入字段发生更改时,它会调用父级上的处理程序来更新状态。 - wintvelt
谢谢,我现在明白了。这是一个用于Stripe的表单,将信用卡信息存储在状态中安全吗?为什么refs不好?我很好奇,因为我完成了两个教程:官方MDG Meteor+React和LevelUpTuts的Meteor+React for Everyone都使用了refs。你能为我解释一下吗? - cocacrave
7个回答

57

您可以在无状态组件内使用ref

这是我示例 fiddle,它展示了如何使用它。

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>
  )
}

注意:虽然这是一种方法,但是除非你确实需要它,否则不建议使用这种方法。试着考虑重新设计你的React代码而不是像这样进行hack。您可以在这里阅读更多相关信息。


6
这应该是被接受的答案。这可能不是最佳设置,但对于无状态函数式控件,如果您想要访问呈现的DOM节点,那么这是截至2017年1月为止最好的选择。 - mwigdahl
https://codepen.io/DZuz14/pen/NyjYQL?editors=0011 展示了React文档推荐的方法。他们明确表示,大多数情况下不建议使用refs,因为这可能会导致在React更新后破坏代码。 - Dan Zuzevich
1
我强烈不同意那个有6个赞的评论。截至2017年1月,这不是最佳方法。更安全的做法是将输入字段存储在父组件状态中。请阅读官方文档以了解处理表单的方法。 - Dan Zuzevich
2
@DanielZuzevich 给一个正确的答案投反对票是对社区的不利影响。这个问题是关于如何使用refs来解决的,而这个答案是正确的。它并没有询问推荐的方法。我们已经知道了。 - Inanc Gumus
把你的答案改成不使用 refs,然后用另一种方式实现。这对社区更有益。 - Dan Zuzevich
显示剩余9条评论

18

截至React 16.8版本,您可以使用useRef hook, 来自文档:

useRef返回一个可变的ref对象,其.current属性初始化为传递的参数(initialValue)。返回的对象将在组件的整个生命周期中保持不变。

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>
    </>
  );
}

无法在react 16.8.6中使其工作。它显示Uncaught TypeError: textFieldRef.current.focus is not a function - Maris
@Maris,你需要重新加载页面才能激活引用,并且在React中必须导入useRef。 - cubefox

16

编辑:看起来这个问题不再存在了,因为自从回答这个问题以来,关于如何处理这种情况的新想法已经出现。请参考 inanc 的答案而不是这个。

在无状态组件中,Refs 不可用。 来自React文档

由于无状态函数没有后备实例,您不能将 Ref 附加到无状态函数组件上。通常这不是问题,因为无状态功能不提供命令式 API。没有命令式 API,您对实例能做的事情也很有限。但是,如果用户想要找到无状态函数组件的 DOM 节点,则必须将该组件包装在有状态组件(例如 ES6 类组件)中,并将 Ref 附加到有状态包装组件上。


1
实际上,你可以从一个无状态组件中引用一个DOM元素。根据文档说明:只要你引用的是一个DOM元素或一个类组件,你就可以在函数式组件内使用ref属性 - Sadok Mtir

4

@inanc,你展示了一种不错的方法,但我提出了一种替代的方式,可以使用事件目标来获取DOM元素的引用。由于您正在使用表单元素,您可以为输入元素命名并使用它来访问表单对象。

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>
  )
}

非常有用的代码片段!谢谢。 - eldorjon

2
您不能在无状态的React组件中使用refs(感谢Moti Azu在文档中的片段+1)。
您可以使用多种技术将东西放入/取出无状态组件(而不使用Ref或使用类组件),我创建了下面的代码片段来说明:
1.如何将内容传递到无状态组件(在父组件中静态设置标签属性)。
2.如何在无状态组件中获取内容,而不使用refs(输入组件)。
请尝试并让我知道您是否仍然有问题,祝您愉快...

// 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>


你的回答有误导性。可能是因为它已经过时了,但无论如何,在无状态组件中确实可以使用refs。您可以在ReactJS文档中阅读有关此的信息,在访问Refs部分的底部:https://reactjs.org/docs/refs-and-the-dom.html#accessing-refs - p-syche
你是在指 forwardRef 的解决方案,还是其他什么? - helcode

2
现在,你应该避免使用ref属性来获取输入字段的值。相反地,你应该使用React的本地状态。ref属性仅保留用于少数用例
  • 管理焦点、文本选择或媒体播放
  • 与第三方DOM库集成
  • 触发命令式动画

-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;

1
请不要仅仅发布代码作为答案,还要提供解释您的代码是如何解决问题的。带有解释的答案通常具有更高的质量,并且更有可能吸引赞同。 - Mark Rotteveel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接