React : getElementById error — can't even understand what is happening

Hello everyone,

I am sharpening my skill with a small react project, an experience point calculator for a tabletop RPG.

It worked fine last time I added code. Today, I started it and it gives me a blank page.

here is the error message:

Uncaught TypeError: document.getElementById(…) is null
calculMan App.js:84
App App.js:100
React 11
workLoop scheduler.development.js:266
flushWork scheduler.development.js:239
performWorkUntilDeadline scheduler.development.js:533
App.js:84
calculMan App.js:84
App App.js:100
React 11
workLoop scheduler.development.js:266
flushWork scheduler.development.js:239
performWorkUntilDeadline scheduler.development.js:533

Here is the code from app.js:

import React from 'react';
import ReactDOM from 'react-dom/client';
import logo from './logo.svg';
import './App.css';



function calculMan(frequence, diff, risque, vaincu) {

  console.log(frequence);
  console.log(diff);
  console.log(risque);
  console.log(vaincu);
  let modFreq = 1;
  switch (frequence) {
    case '1':
      modFreq = 5;
      break;
    case '2':
      modFreq = 2.5;
      break;
    case 'routine':
      modFreq = 0.5;
      break;
    default:
      modFreq = 1;
  }

  let modRisque = 1;
  switch (risque) {
    case 'aucunRisque':
      modRisque = 0.5;
      break;
    case 'risque':
      modRisque = 1;
      break;
    case 'trisque':
      modRisque = 2;
      break;
    case 'eRisque':
      modRisque = 3;
      break;
    default:
      modRisque = 1;
  }

  let modVaincu = 1;
  if (vaincu) {
    modVaincu = 5;
  }

  let modTotal = modVaincu * modRisque * modFreq;

  let expBase = 0;

  switch (diff) {
    case 'tFacile':
      expBase = 5;
      break;
    case 'facile':
      expBase = 10;
      break;
    case 'moyenne':
      expBase = 50;
      break;
    case 'difficile':
      expBase = 100
      break;
    case 'tDifficile':
      expBase = 150;
      break;
    case 'eDifficile':
      expBase = 200;
      break;
    case 'folie':
      expBase = 300;
      break;
    case 'absurde':
      expBase = 500;
  }

  let expFinal = expBase * modTotal;
  
  document.getElementById("resultatMan").innerHTML = `${expBase} * ${modTotal} = ${expFinal} points d'expérience`;
}








function App() {
  const [frequence, setFrequence] = React.useState('normale');
  const [diff, setDiff] = React.useState('moyenne');
  const [risque, setRisque] = React.useState('risque');
  const [vaincu, setVaincu] = React.useState(false);

  calculMan(frequence, diff, risque, vaincu);

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <h1>
          Calculateur d'expérience pour Rolemaster
        </h1>

        <form>
          <div id="section-frequence">
            <label htmlFor="frequence">Fréquence de l’action : </label>
            <select value={frequence} id="frequence" onChange={event =>{setFrequence(event.target.value)}}>
              <option value="normale">Normale</option>
              <option value="1">1ere fois</option>
              <option value="2">2e fois</option>
              <option value="routine">Routine</option>
            </select>
          </div>

          <div id="section-diff">
            <label htmlFor="diff">Difficulté : </label>
            <select value={diff} id="diff" onChange={event =>{setDiff(event.target.value)}}>
              <option value="tFacile">Très facile</option>
              <option value="facile">Facile</option>
              <option value="moyenne">Moyenne</option>
              <option value="difficile">Difficile</option>
              <option value="tDifficile">Très difficile</option>
              <option value="eDifficile">Extrêmement difficile</option>
              <option value="folie">Folie</option>
              <option value="absurde">Absurde</option>
            </select>
          </div>

          <div id="section-risques">
            <label htmlFor="risques">Risque : </label>
            <select value={risque} id="risques" onChange={event =>{setRisque(event.target.value)}}>
              <option value="aucunRisque">Aucun risque</option>
              <option value ="risque">Risqué</option>
              <option value="tRisque">Très risqué</option>
              <option value="eRisque">Extrêmement risqué</option>
            </select>
          </div>

          <div id="section-vaincu">
            <label htmlFor="vaincu">Adversaire vaincu</label>
            <input type="checkbox" id="vaincu" checked={vaincu} onChange={event =>{setVaincu(event.target.checked)}}></input>
          </div>
        </form>

        <p id="resultatMan"></p>

        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}


export default App;

Here is the code from index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

Thanks for helping me, I really can’t find what is wrong.

You call the line document.getElementById("resultatMan")... in your calculMan function. And you call calculMan in App.js before you return <p id="resultatMan"></p>. So the element #resultatMan does not yet exist when you call document.getElementById() in calculMan.

Generally, it is not recommended to work with the DOM this way throughout the React app. Better use the useRef hook from React like so:

const resultManRef = useRef(null);
return (<p ref={resultManRef}></p>)

This still does not solve the problem, though.
It is better not to use DOM elements in functions like that. Let the function calculMan return just the string like so:

function calculMan(frequence, diff, risque, vaincu) {
 ...
 return `${expBase} * ${modTotal} = ${expFinal} points d'expérience`
}

And then render the p tag with the string as a child like so:

function App() {
  ...
  const pText = calculMan(frequence, diff, risque, vaincu)
  return (<p>{pText}</p>)
}
2 Likes

Thanks a lot. It works like a charm.

2 Likes