使用 React.createRef 时,current 总是为 null。

52

我试图实现这个目标

我一定漏掉了什么,但是我不明白为什么在这个例子中current总是null

class App extends React.PureComponent {
  constructor(props) {
    super(props);
    this.test = React.createRef();
  }
  render() {
    return <div className="App">current value : {this.test.current + ""}</div>;
  }
}

您可以在这里查看我的测试用例。

8个回答

51
因为您忘记将ref分配给某个DOM元素。 您只是在创建它。 请按照以下方式编写:
``````
class App extends React.PureComponent {
  constructor(props) {
    super(props);
    this.test = React.createRef();
  }

  handleClick = () => alert(this.test.current.value)

  render() {
    return (
      <div className="App">
        <input ref={this.test} />
        <button onClick={this.handleClick}>Get Value</button>
      </div>
    )
  }
}

示例代码。


4
我知道这个问题早已有答案,但你提供的工作示例显示“当前值:null”。它不应该给出一个值吗? - Darin Cardin
为什么我们需要在构造函数中分配引用?在我的情况下,我没有构造函数。我初始化了引用,但它返回为空。 - Artexias

17

我知道这不是 OP 问题的解决方案,但对于那些从 Google 搜索而来的人来说,ref.current 可能为 null 的一种方式是将其附加到的组件是连接的组件,就像使用 react-redux connect 或 withRouter。对于 react-redux,解决方案是在 connect 的第四个选项中传递 forwardRef:true。


13

React.createRef()是异步的,所以如果您尝试在componentDidMount中访问ref,则会返回null,稍后会返回引用的组件的属性。

componentDidMount(): void {
      if (this.viewShot && this.viewShot.current) {
          this.viewShot.current.capture().then((uri) => {
        console.log('do something with ', uri);
          });
      }
  }

这是在此情境下使用React.createRef()的正确方式。


1
你完全错了!React 在 componentDidMount 触发之前会更新所有的 DOM 和 refs。什么是 capture 方法?'current' 元素和 input 元素都不存在这个方法。 我就是想不明白为什么有这么多好评。PS:已接受的答案正确! - sandbox992

8

你缺少了 ref={this.test} 属性。

return (
  <div className="App" ref={this.test}>
    current value : {this.test.current + ""}
  </div>
);

8
这可能会在以下情况下发生:
  • 您忘记将 ref传递给组件,例如从问题中的 this.test

<Component ref={this.test} />

  • 如果您正在使用 Redux ,并且传递ref props的组件被包装在connect方法中,因此this.test.current会返回null ,因为它指向Redux包装器,要使此类组件有效,请将 forwardRef:true

即:connect(mapStateToProps, mapDispatchToProps, null, {forwardRef: true})(Component)

  • 如果您同时使用withRouterconnect,则这里会有两个wrapper而不是一个,因此this.test.current显然将返回null ,要克服这一点,请确保withRouter 包装connect

withRouter(connect(mapStateToProps, mapDispatchToProps, null, {forwardRef: true})(Component))

然后

<Component wrappedComponentRef={ref => this.test = ref} />

wrappedComponentRef是用于使包装组件可用的属性, 就像forwardRef:true 一样,您可以在文档这里找到它。


我正在使用 withTranslation 来连接我的类组件。无法传递 {forwardRef: true} 这个参数。你有任何想法如何解决吗? - Samiksha Jagtap
很棒能在一个答案中拥有所有这些东西。 - Rik Schoonbeek

3

在 React 的版本 17.0.2 中,引用(refs)和异步方面有所变化。在更新之后,像下面这样的代码就不能正常工作了:

import {useRef} from 'react';
import './kind.css'

const Kind = ({prop}) => {

    // defining the ref
    const kindRef = useRef('')
    
    // print the ref to the console
    console.log(kindRef)


    return (
    <div ref={kindRef} className="kind-container" >
        <div className="kind" data-kind='drink'>Drink</div>
        <div className="kind" data-kind='sweet'>Sweet</div>
        <div className="kind" data-kind='lorem'>lorem</div>
        <div className="kind" data-kind='ipsum'>ipsum</div>
        <div className="kind" data-kind='ipsum' >food</div>
    </div>
    );
}

export default Kind;

在初始化引用后,将其分配给dom需要一些时间。JavaScript是一种同步语言,不会等待引用初始化,而会直接跳过到日志。

为了解决这个问题,我们需要像这样使用useEffect

import { useRef, useEffect} from 'react';
import './kind.css'

const Kind = ({prop}) => {

    // defining the ref
    const kindRef = useRef('')
    
    // using 'useEffect' will help us to do what ever you want to your ref varaible,
    // by waiting to letting the elements to mount:'mount means after the elements get inserted in the page' and then do what you want the ref
    useEffect(()=>{

        // print the ref to the console
        console.log(kindRef)        

    })


    return (
    <div ref={kindRef} className="kind-container" >
        <div className="kind" data-kind='drink'>Drink</div>
        <div className="kind" data-kind='sweet'>Sweet</div>
        <div className="kind" data-kind='lorem'>lorem</div>
        <div className="kind" data-kind='ipsum'>ipsum</div>
        <div className="kind" data-kind='ipsum' >food</div>
    </div>
    );
}

export default Kind;

useEffect等待引用被分配给DOM元素,然后运行分配给它的函数。


2
React文档会鼓励你在这种特定情况下使用useLayoutEffect。此外,记录kindRef.current可能更安全,因为控制台日志不是静态的。最后,在像useEffect这样的钩子中始终指定依赖项数组可能是一个好习惯。 - Sharcoux
1
正如您所注意到的,我编辑了您的问题以更好地符合回答指南。您原来的答案包含了几个语法错误。您还特别要求点赞,这也是违反规则的行为。最后,您在代码块中无意义地签署了您的名字。您的用户名显示在答案底部右下角(评论上方)。请不要将您的答案回滚到原始版本;我已经进行了更改,使您的答案符合指南并防止其被删除。 - Rojo
谢谢,兄弟。我很匆忙。 - ammar khaled

1

如果你在使用useCallback(或另一个hook)中的ref,请记得将ref添加到依赖项中:

const doSomething = useCallback(() => {
 ref.current?.doIt();
}, [ref]);

我已经绞尽脑汁5个小时了,我只需要把这个完成。非常感谢。这是一位后端开发人员尝试编写React的报告。 - Eduardo Mior

-2
<div ref={this.test}>

你必须将 ref 属性分配给你的 DOM 元素。 在这种情况下,你必须将 ref 分配给 div 元素。


4
这个回答对所选答案有什么补充呢? - Sharcoux

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