Search code examples
javascriptreactjstypescriptref

How do you get the name of a ref?


Say you have this JSX:

<div ref={this.divRef}>
  <SomeComponent />
</div>

And you want to get the name of the ref and add it to your state, how would you do that?

Something like:

componentDidMount() {
    // get all the positions of the current sections
    if (this.divRef.current) {
      this.setState({
        breakpoints: [{
          name: this.divRef.current.name,
          position: this.divRef.current.getBoundingClientRect().bottom,
        }],
      }, () => {
          // tslint:disable-next-line:no-console
          console.log(this.divRef.current, 'this.divRef.current')
      })
    }
  }

Solution

  • In a comment you've said:

    I want to assign each ref a name so I can use it later on

    If you really mean the ref, the only reliable way I can think of to do that is with a WeakMap keyed by the ref. (Although you could use a Symbol-named property, more below.)

    Create the WeakMap in your constructor (or in your module, I don't know how much code needs to be able to use these names);

    this.nameMap = new WeakMap();
    

    And then when you need to get/assign the name:

    let name = this.nameMap.get(this.divRef);
    if (!name) {
        name = "some appropriate name";
        this.nameMap.set(this.divRef, name);
    }
    

    You could use a Symbol-named property, to store the name on the ref object itself, but in general storing arbitrary properties on objects your code doesn't control isn't a good idea. But for completeness, you'd create the symbol in your constructor (or module):

    this.nameKey = Symbol("refname");
    

    then use it:

    // I don't recommend this
    let name = this.divRef[this.nameKey];
    if (!name) {
        name = "some appropriate name";
        this.divRef[this.nameKey] = name;
    }
    

    But I don't recommend it. An environment with Symbol will also have WeakMap, which I'd use here.


    But, if you mean the element the ref is currently referring to, you could use a data-* attribute for that:

    // Assuming you've checked that `current` isn't `null`
    let name = this.divRef.getAttribute("data-name");
    if (!name) {
        name = "some appropriate name";
        this.divRef.setAttribute("data-name", name);
    }
    

    If you can assume dataset support, you could use dataset instead:

    // Assuming you've checked that `current` isn't `null`
    let name = this.divRef.dataset.name;
    if (!name) {
        name = "some appropriate name";
        this.divRef.dataset.name = name;
    }
    

    That said, the WeakMap solution would work just as well for elements as for refs:

    let name = this.nameMap.get(this.divRef.current);
    if (!name) {
        name = "some appropriate name";
        this.nameMap.set(this.divRef.current, name);
    }