Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refs for dynamically generated components in React?

The code below is a minimal working example to explain my problem. This code generates 3 Note components using Array.map and when you hit enter in them it empties the statically generated Note component above them using a ref to its DOM element.

What I want to do is instead have the Note you hit enter in empty itself. I think this would require generating a dynamic array containing {id, ref} for each Note, so I could pass the Note id to handleKeyDown then search the refs array for that id and use the corresponding ref to change the DOM element. But I'm having trouble figuring out how to actually do this.

I realize using refs here isn't necessary but it's just an example since my actual code is a lot longer and calls focus().

import React, { Component } from "react";
import "./App.css";

class App extends Component {
  constructor() {
    super();
    this.notes = [
      { text: "Hello world 1", id: 1 },
      { text: "Hello world 2", id: 2 },
      { text: "Hello world 3", id: 3 }
    ];
    this.ref = React.createRef();
  }

  handleKeyDown = event => {
    if (event.key === "Enter") {
      event.preventDefault();
      this.ref.current.textContent = "";
    }
  };

  render() {
    return (
      <div>
        <Note
          text={"StaticNote"}
          key={0}
          id={0}
          handleKeyDown={this.handleKeyDown}
          innerRef={this.ref}
        />
        {this.notes.map(note => (
          <Note
            text={note.text}
            key={note.id}
            id={note.id}
            handleKeyDown={this.handleKeyDown}
          />
        ))}
      </div>
    );
  }
}

class Note extends Component {
  render() {
    return (
      <p
        ref={this.props.innerRef}
        contentEditable
        onKeyDown={this.props.handleKeyDown}
      >
        {this.props.text}
      </p>
    );
  }
}

export default App;
like image 877
pyjamas Avatar asked Nov 19 '18 05:11

pyjamas


1 Answers

You need to store a separate ref for all your Note components and then pass back the index of the Note in focus to the handleKeyDown function

import React, { Component } from "react";
import "./App.css";

class App extends Component {
  constructor() {
    super();
    this.notes = [
      { text: "Hello world 1", id: 1, ref: React.createRef() },
      { text: "Hello world 2", id: 2, ref: React.createRef() },
      { text: "Hello world 3", id: 3, ref: React.createRef() }
    ];
  }

  handleKeyDown = (event, index) => {
    if (event.key === "Enter") {
      event.preventDefault();
      this.notes[index].ref.current.textContent = "";
    }
  };

  render() {
    return (
      <div>
        <Note
          text={"StaticNote"}
          key={0}
          id={0}
          handleKeyDown={this.handleKeyDown}
          innerRef={this.ref}
        />
        {this.notes.map((note, index) => (
          <Note
            index={index}
            innerRef = {note.ref}
            text={note.text}
            key={note.id}
            id={note.id}
            handleKeyDown={this.handleKeyDown}
          />
        ))}
      </div>
    );
  }
}

class Note extends Component {
  render() {
    return (
      <p
        ref={this.props.innerRef}
        contentEditable
        onKeyDown={(e) => this.props.handleKeyDown(this.props.index)}
      >
        {this.props.text}
      </p>
    );
  }
}

export default App;
like image 58
Shubham Khatri Avatar answered Sep 28 '22 07:09

Shubham Khatri