skip to Main Content

Here’s a page where I am importing my vanilla JS file:

import React from 'react';
import '../js/accountAdmin.js';


const Home = () => {

  return (
    <div className="app">
      <section className="side-bar">
        <nav>
          <p>My Page</p>
        </nav>
      </section>

      <section className="main">

        <h1>My Page</h1>
        <span>Click on a cell to edit:</span>

        <div className="accountsTableDiv"></div>
        <div className="addAccount"></div>
        <div className="feedbackMessages"></div>


        <div className="bottom-section">
          <p className="info">
            Frontend for MyPage.
          </p>
        </div>

      </section>
    </div>
  );
};

export default Home;

Here’s the JS file:

let tblBody;
const url = "http://localhost:8080/api/v1/accounts";

//wait until the page loads.
window.onload = async function() {
    let feedbackMessage = document.querySelector(".feedbackMessages");
    let accountsTableDiv = document.querySelector(".accountsTableDiv");
    let accountAddDiv = document.querySelector(".addAccount");
    
    let table = document.createElement('table');

    console.log(feedbackMessage);       //null on refresh, works fine on hard reload
    console.log(accountsTableDiv);      //null on refresh, works fine on hard reload
    console.log(accountAddDiv);         //null on refresh, works fine on hard reload
    console.log(table);

    tblBody = document.createElement('tbody');
    console.log(tblBody);

    console.log("onload() ended");
};

The document.querySelector() statements are returning null on refresh, but they work as expected on hard reload. I’m guessing this has something to do with React’s virtual DOM, but I’m not sure what to do about it. What should I do? What do I need to understand about React when it comes to using vanilla JS?

2

Answers


  1. It’s not advised to manipulate DOM elements directly using JavaScript code or jQuery because they are managed by React. So if you want to access an element you could use a ref to the DOM node as it is explained here.
    However, in some cases, you can avoid accessing DOM elements by combining useState, useEffect and Conditional Rendering:

    const MyComp = ()=>{
       const [data, setData] = useState()
    
       useEffect(()=>{
          // Fetch your data here and store it in data state to re-render
       ,[]}
    
       return(
          <div>
            { data && <AnotherComponent fetchedData={data} /> } //In this way, AnotherComponent will render only if there is data
          </div>
       )
       
    }
    
    Login or Signup to reply.
  2. The most common advice you are going to hear is not to manipulate DOM manually in React app (and based on your code snippets your vanilla JS code can easily be refactored into standard React code). However, according to the React documentation, manual DOM manipulation can safely be used as long as there are no conflicting changes.

    Avoid changing DOM nodes managed by React. Modifying, adding children to, or removing children from elements that are managed by React can lead to inconsistent visual results or crashes like above.

    However, this doesn’t mean that you can’t do it at all. It requires caution. You can safely modify parts of the DOM that React has no reason to update. For example, if some <div> is always empty in the JSX, React won’t have a reason to touch its children list. Therefore, it is safe to manually add or remove elements there.

    https://react.dev/learn/manipulating-the-dom-with-refs#best-practices-for-dom-manipulation-with-refs

    While I was unable to reproduce your specific issue, I suspect there may be a race condition between window.onload and React rendering your component. To guarantee the right execution order, I can suggest the following approach:

    1. Declare your vanilla JS function as a global function
    2. Call your global function from React effect (this way you’ll know for sure your target <div> is already present in DOM)
    export default function App() {
      // eslint-disable-next-line no-undef
      React.useEffect(loadAccounts, []);
      
      // ...
    }
    
    window.loadAccounts = function loadAccounts() {
      const accountsTableDiv = document.querySelector(".accountsTableDiv");
    
      // React effects fire twice in React 18 dev mode
      if (accountsTableDiv.children.length === 0) {
        // ...
        accountsTableDiv.appendChild(...);
      }
    };
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search