简体   繁体   中英

using insertAdjacentHTML with a React Component

So I'm trying to recreate the Github contribution grid using React.

I'm trying to use insertAdjacentHTML to populate the grid container with a bunch of divs that I can then style.

However, React is throwing me an error "TypeError: Cannot read property 'insertAdjacentHTML' of null"

Here is my simple code:

export default function NewHabit() {
  const gridContainer = document.querySelector('.grid-container');

  for (let i = 0; i < 365; i++) {
    gridContainer.insertAdjacentHTML('beforeend', `<div id="box></div>`);
  }

  return (
    <div>
      <div className="grid-container"></div>
    </div>
  );
}

the component is not rendered yet when NewHabit starts to execute.
you have to put it after componentDidMount or useEffect(() => {}, []) , they execute after the component mounts and has been added to the DOM.

besides, it's not safe to do this using insertAdjacentHTML/Elmenent . react has a virtual dom that does not keep this type of change in it. after a rerender, it might render what it had in the virtual dom. for example, an empty grid. it's better to add your boxes in react way.

Solved it by just doing it the React way:

export default function NewHabit() {
  let grid = [];
  for (let i = 0; i < 365; i++) {
    grid.push(<div className="box"></div>);
  }

  return (
    <div>
      <div className="grid-container">{grid}</div>
    </div>
  );
}

First of all, read carefully the Refs and the DOM part of the React documentation. The important thing you should learn is the fact that React is working using virtual DOM, so you don't have a direct access to the browser's DOM.

Second, I really discourage you from the direct DOM manipulation. React offers you a number of options to render html dynamically (actually, it renders dynamically all the time). For example, instead of inserting html you may use lists and keys :

export default function NewHabit() {
  let gridElements = []
  for (let i = 0; i < 365; i++) {
    gridElements.push(<div id="box" key={i}></div>)
  }

  return (
    <div>
      <div className="grid-container">{gridElements}</div>
    </div>
  );
}

But if you really want the direct DOM access, just use ref:

import {useRef} from 'react'

export default function NewHabit() {
  const containerRef= useRef(null);

  if (containerRef.current) {
    for (let i = 0; i < 365; i++) {
      containerRef.current.insertAdjacentHTML('beforeend', `<div id="box></div>`);
    }
  }

  return (
    <div>
      <div className="grid-container" ref={containerRef}></div>
    </div>
  );
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM