如何在React中访问DOM元素?在React中,相当于document.getElementById()的是什么?

347

我应该如何在 react.js 中选取特定的柱形图?

这是我的代码:

var Progressbar = React.createClass({
    getInitialState: function () {
        return { completed: this.props.completed };
    },
    addPrecent: function (value) {
        this.props.completed += value;
        this.setState({ completed: this.props.completed });
    },

    render: function () {

        var completed = this.props.completed;
        if (completed < 0) { completed = 0 };


        return (...);
    }

我想使用这个React组件:

var App = React.createClass({
    getInitialState: function () {

        return { baction: 'Progress1' };
    },
    handleChange: function (e) {
        var value = e.target.value;
        console.log(value);
        this.setState({ baction: value });
    },
    handleClick10: function (e) {
        console.log('You clicked: ', this.state.baction);
        document.getElementById(this.state.baction).addPrecent(10);
    },
    render: function () {
        return (
            <div class="center">Progress Bars Demo
            <Progressbar completed={25} id="Progress1" />
                <h2 class="center"></h2>
                <Progressbar completed={50} id="Progress2" />
                <h2 class="center"></h2>
                <Progressbar completed={75} id="Progress3" />
                <h2 class="center"></h2>
                <span>
                    <select name='selectbar' id='selectbar' value={this.state.baction} onChange={this.handleChange}>
                        <option value="Progress1">#Progress1</option>
                        <option value="Progress2">#Progress2</option>
                        <option value="Progress3">#Progress3</option>
                    </select>
                    <input type="button" onClick={this.handleClick10} value="+10" />
                    <button>+25</button>
                    <button>-10</button>
                    <button>-25</button>
                </span>
            </div>
        )
    }
});

我想执行 handleClick10 函数并对我选择的进度条执行操作。但是我得到的结果是:

 You clicked:  Progress1
 TypeError: document.getElementById(...) is null

我如何在React.js中选择特定的元素?

9个回答

296

通过指定 ref,您可以这样做。

编辑:在使用函数组件的 React v16.8.0 中,您可以使用 useRef 定义一个 ref。请注意,在函数组件上指定 ref 时,需要使用 React.forwardRef 将其转发到使用 useImperativeHandle 暴露函数的 DOM 元素上。

例如:

const Child1 = React.forwardRef((props, ref) => {
    return <div ref={ref}>Child1</div> 
});

const Child2 = React.forwardRef((props, ref) => {
    const handleClick= () =>{};
    useImperativeHandle(ref,() => ({
       handleClick
    }))
    return <div>Child2</div> 
});
const App = () => {
    const child1 = useRef(null);
    const child2 = useRef(null);

    return (
        <>
           <Child1 ref={child1} />
           <Child1 ref={child1} />
        </>
    )
}

编辑:

React 16.3+ 中,使用 React.createRef() 来创建你的 ref:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}

要访问该元素,请使用:

const node = this.myRef.current;

使用 React.createRef() 的文档


编辑

然而 Facebook 不建议使用字符串引用,因为它们存在一些问题,被认为是过时的,并有可能在未来的版本中被删除。

从文档中可以看到:

过时的 API:字符串引用

如果您之前使用过 React,那么您可能熟悉旧版API, 其中 ref 属性是一个字符串,如“textInput”,DOM节点通过 this.refs.textInput 访问。 我们不建议使用此方式,因为字符串引用存在某些问题,被认为是过时的,并有可能在未来的版本中被删除。 如果您目前正在使用 this.refs.textInput 来访问引用,请改用回调模式。

React 16.2 及更早版本的推荐方式是使用回调模式:

<Progressbar completed={25} id="Progress1" ref={(input) => {this.Progress[0] = input }}/>

<h2 class="center"></h2>

<Progressbar completed={50} id="Progress2" ref={(input) => {this.Progress[1] = input }}/>

  <h2 class="center"></h2>

<Progressbar completed={75} id="Progress3" ref={(input) => {this.Progress[2] = input }}/>

使用回调的文档


即使较旧的版本中,Refs也是使用字符串来定义的,如下所示:

<Progressbar completed={25} id="Progress1" ref="Progress1"/>

    <h2 class="center"></h2>

    <Progressbar completed={50} id="Progress2" ref="Progress2"/>

      <h2 class="center"></h2>

    <Progressbar completed={75} id="Progress3" ref="Progress3"/>

要获取元素,只需执行以下操作

var object = this.refs.Progress1;

记得在箭头函数块中使用this,像这样:

print = () => {
  var object = this.refs.Progress1;  
}

等等,等等...


5
Facebook建议不采用这种方法。请参见此处https://facebook.github.io/react/docs/refs-and-the-dom.html。 - Dmitry
4
根据我所看到的,上述页面是针对v15.4.2及以后版本编写的,我猜在我写答案的时候这不是那里面本来就有的。无论如何,我已经用正确的方法编辑了答案。 - Shubham Khatri
2
@MattSidor,感谢您的编辑,您为我节省了一些时间 :-) - Shubham Khatri
你会考虑将当前的React 16.3+部分移动到顶部吗? - Michael Freidgeim
@MiFreidgeimSO-stopbeingevil,我更新了我的答案。感谢您的建议。 - Shubham Khatri
显示剩余6条评论

58

要在React中获取元素,您需要使用ref,并且可以在函数内部使用ReactDOM.findDOMNode方法。

但我更喜欢的是直接在事件中调用ref

<input type="text" ref={ref => this.myTextInput = ref} />

这是一些好的链接,可以帮助你弄清楚。


2
这是正确的做法。它会在对象/类上添加一个元素的引用,这样你就可以简单地使用 this.myTextInput 来访问它。 - Sam Parmenter
2
如何使用动态创建的元素来实现这个功能? - Sagar Kodte
3
这个在函数式组件中不起作用:https://reactjs.org/docs/refs-and-the-dom.html#accessing-refs - Neurion
1
@Neurion 谢谢你提供的链接。它指导我们如何在函数组件中实现这个功能。 - Sapthaka
当您创建一个ref时,是否必须将该引用传递到子树或使用useContext才能在任何其他地方使用它?如果是这样,您的引用需要成为组件的父级,这是非常愚蠢、受限和封装的...我不能通过调用获取所有引用来从任何地方获取它。 - ii iml0sto1
显示剩余2条评论

53

使用更新版本的React,您可以通过类似这样的钩子来使用和操作DOM:

import React, { useEffect, useRef } from "react";

const MyComponent = () => {
  const myContainer = useRef(null);
  
  useEffect(() => {
    console.log("myContainer..", myContainer.current);
  });

  return (
    <>
      <h1>Ref with react</h1>
      <div ref={myContainer}>I can use the DOM with react ref</div>
    </>
  );
};

export default MyComponent;

每当你想要访问你的DOM元素时,只需要使用myContainer.current


9
每当你想访问你的DOM元素时,只需使用myContainer.current - Delmo
当你创建一个ref时,必须将该引用传递到子树或使用useContext才能在任何其他地方使用它吗?如果是这样,你的引用需要是组件的父级,这非常愚蠢、受限和封装的...我不能通过调用获取所有引用来从任何地方获取它。 - ii iml0sto1
如果你正在使用TypeScript,将useRef的类型更改为useRef<HTMLDivElement | null>(null)以访问引用元素的对象。 - undefined

17
你可以替换 <\p>
document.getElementById(this.state.baction).addPrecent(10);

随着

this.refs[this.state.baction].addPrecent(10);


  <Progressbar completed={25} ref="Progress1" id="Progress1"/>

22
如何在动态创建的元素中实现此操作? - Sagar Kodte

5

免责声明:虽然最高评分的答案可能是更好的解决方案,但对于初学者来说,当您想要非常简单的东西时,这可能会让人感到困惑。本文旨在回答您最初的问题“如何在React中选择某些元素”,并直接回答。

我认为您的问题中的混淆是因为您有一个React 组件,您正在传递id“Progress1”,“Progress2”等。 我相信这不是设置html属性'id',而是React组件属性。例如:

class ProgressBar extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            id: this.props.id    <--- ID set from <ProgressBar id="Progress1"/>
        }
    }        
}

如上面的一些答案中提到的,您绝对可以在React应用程序中使用 document.querySelector,但必须明确它正在选择您组件的渲染方法的HTML输出。因此,假设您的渲染输出如下:

render () {
    const id = this.state.id
    return (<div id={"progress-bar-" + id}></div>)
}

然后您可以在其他地方使用普通的JavaScript查询选择器,如下所示:

let element = document.querySelector('#progress-bar-Progress1')

4
你需要在类组件和函数式组件中遵循两种不同的方式来实现它。
  1. For class components

    <input type="text" ref={ref => this.myTextInput = ref} />
    

看上面的代码。使用 "ref" 属性引用相关元素。然后,您将能够使用该引用来引用该元素。在本例中,我可以使用 "this.myTextInput" 来引用上面的输入元素。

  1. For functional components

    const textInput = useRef(null)
    
使用"useRef"钩子,并将该变量名称设置为要引用的元素的"ref"属性的值(如下所示)。
<input type="text" ref={textInput} />

一个关于函数式组件的例子。
import React, {useRef} from 'react'

function CustomTextInput(props) {
  // textInput must be declared here so the ref can refer to it
  const textInput = useRef(null);

  function handleClick() {
     textInput.current.focus();
  }

  return (
    <div>
     <input type="text" ref={textInput} />
    </div>
  );
}

想要学习更多? 请戳这里


-1

由于React使用JSX代码创建HTML,我们不能使用常规方法如documment.querySelector或getElementById来引用dom。

相反,我们可以使用React ref系统来访问和操作Dom,如下面的示例所示:

constructor(props){

    super(props);
    this.imageRef = React.createRef(); // create react ref
}

componentDidMount(){

    **console.log(this.imageRef)** // acessing the attributes of img tag when dom loads
}


render = (props) => {

const {urls,description} = this.props.image;
    return (

            <img
             **ref = {this.imageRef} // assign the ref of img tag here**
             src = {urls.regular} 
             alt = {description}
             />

        );

}

}


6
这并不是真的。你可以在img元素上添加一个id,使用document.getElementById获取它,在大多数情况下,它能够正常工作。这可能会导致问题/使你的代码更难调试,但React/JSX并没有阻止你使用本地DOM方法。 - adam tropp
1
+1 因为这展示了一种非常简单和简洁的使用 ref 的方式。然而,我想提供一个建议,你添加的 ** 指示符会让没有经验的用户认为你的代码有问题(因为从技术上讲,它确实有问题),并且会产生不必要的负评,所以考虑删除它们。另外请注意,说你不能使用 DOM 元素是不正确的 - 你不应该,但你实际上是可以的。我更愿意将那行改成类似于“在 React 中使用 DOM 引用元素很困难,也是不好的做法,因此我们使用 refs [...]”。 - aggregate1166877

-4
在我的情况下,我无法使用ref,因为元素位于许多子组件之间,我必须通过类和ID访问它们,而不是通过ref。因此,尝试使用useEffect钩子无法找到该元素:
useEffect(() => {
  const el1 = document.querySelector('.el1')
  const el2 = document.querySelector('.el2')
}, [])

元素是undefined,因为在挂载它时,子组件也没有在父组件之前挂载。

所以,我使用了超时:

useEffect(() => {
  const timer = setTimeout(() => {
    const el1 = document.querySelector('.el1')
    const el2 = document.querySelector('.el2')
  },500)
  return () => {
    clearTimeout(timer)
  }
}, [])

现在,它运行得很好。它找到了DOM,我能够对其进行操作。希望这对某人有所帮助!

8
让你的应用程序(以及用户!)等待500毫秒才更新似乎是一个不好的解决方案。显然,这会给用户增加延迟,并且这个解决方案仍然很脆弱,因为它假设你需要访问的其他组件将在500毫秒内被渲染完毕。 - Hartley Brody
@HartleyBrody 这将在渲染钩子之前运行。因此,不会有延迟。它只是等待dom可用。我对这种方法没有任何问题。我明确表示无法使用ref,因此应用了这个技巧并在此发布,这将有助于我遇到的这些情况。 - Bhojendra Rauniyar
2
在函数组件的主体部分(称为React的渲染阶段)中不允许使用突变、订阅、定时器、日志记录和其他副作用。这样做会导致UI中出现混乱的错误和不一致性。这是来自useEffect的React文档的摘录。不要在函数主体内使用副作用! - Oliver Tworkowski
这段代码的问题在于它无法进行测试,也不具备未来的可扩展性。它只能在特定速度和负载能力的计算机上运行,而对于其他计算机则不能。一旦有人添加了新的、更复杂的代码,就可能会导致严重的错误,这些错误很少出现,几乎不可能找到。 - aggregate1166877
你尝试过获取最外层元素root的回调引用(即该子树的根节点),并在回调函数中使用root.querySelector()来获取所有你想要的子元素和孙子元素吗? - Martin

-8

在React中,document.getElementById()的等效方法是document.querySelector()


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