使用getElementById修改元素innerHTML时触发TypeError: Cannot set property 'innerHTML' of null错误的排查与方案咨询
Let's break down what's going wrong here and fix it the React way—since you're using React, direct DOM manipulation like getElementById and innerHTML is almost never the right approach, and that's exactly why you're hitting this error.
Why You're Getting TypeError: Cannot set property 'innerHTML' of null
Let's unpack the root causes:
Your
onClickis firing immediately, not on user click
WritingonClick={togglePopup(player.username)}runs the function as soon as the component renders, not when the button is clicked. This triggers state updates prematurely, and when you try to calldocument.getElementById(name)right after, the DOM element might not exist yet (or the state hasn't updated to reflect the change), so it returnsnull.You can't inject JSX via
innerHTML
JSX compiles to React element objects, not raw HTML strings. Shoving JSX intoinnerHTMLwon't render it correctly—it'll just treat it as plain text (or throw errors) because React isn't handling the rendering.React state updates are asynchronous
When you callsetIsOpen(!isOpen), the state doesn't update instantly. Trying to manipulate the DOM right after this call means you're working with the old state, which doesn't match the current DOM state.
The Correct React Approach: Use State to Control Rendering
Instead of fighting React by manipulating the DOM directly, leverage React's declarative model—describe what UI should show based on state, and let React handle the rest. Here's how to fix your code:
Step 1: Update State to Track Active Popup
Instead of a boolean isOpen, use a state variable to track which player's popup is open (since you have multiple players):
import { useState } from 'react'; // Track the username of the player whose popup is active (null = no popup open) const [activePopupUsername, setActivePopupUsername] = useState(null); const togglePopup = (username) => { // Toggle: if the popup is already open for this user, close it; else open it setActivePopupUsername(prev => prev === username ? null : username); }; // Update handleKick to close the popup after action const handleKick = (playerId) => { // Add your kick logic here (API call, state update, etc.) console.log(`Kicking player with ID: ${playerId}`); // Close the popup once action is done setActivePopupUsername(null); };
Step 2: Rewrite the Render Logic
Remove all DOM manipulation code, and render the popup conditionally based on the activePopupUsername state:
return ( <> <legend className="text-xl mb-5 border-yellow-500 border-l-4 pl-2 mt-10">Team Members</legend> <div className="flex gap-5 flex-col w-full mb-4"> {props.team.players.map((player, key) => ( <div key={key} className=""> <label htmlFor="csgoign" className="flex bg-gray-50 hover:bg-gray-200 p-3 rounded-xl w-full items-center justify-between"> <div className="flex font-medium gap-3"> <img src={`https://robohash.org/${player.username}?set=set5`} className="w-12 h-12 rounded-3xl bg-blue-100" alt={`Avatar for ${player.username}`} /> <div className="ml-3"> <a href={`http://localhost:3000/userprofile/${player._id}`} className="text-2xl w-auto font-semibold hover:text-yellow-600" > {player.username} </a> <p className="text-sm font-normal text-gray-700 font-paragraph"> Member since : {player.join_date} </p> </div> </div> {/* Only show kick button/popup if current user is team cap and not viewing their own profile */} {props.user._id === props.team.team_cap && player.username !== props.user.username && ( <div> {activePopupUsername !== player.username ? ( // Show Kick button when no popup is active for this player <button type="button" onClick={() => togglePopup(player.username)} className="border font-semibold text-xl border-red-300 rounded-xl hover:bg-red-600 px-7 py-2" > Kick </button> ) : ( // Show popup when this player's popup is active <div className="popup-box"> <div className="flex flex-col box gap-1"> <div> <p className="font-medium text-lg">Are you sure you want to kick {player.username} ?</p> </div> <div className="flex flex-row gap-14 items-center justify-center"> <button type="button" onClick={() => handleKick(player._id)} className="border font-semibold text-lg border-red-300 rounded-xl hover:bg-red-600 px-6" > Yes </button> <button onClick={() => togglePopup(player.username)} className="border font-semibold text-lg border-yellow-300 rounded-xl hover:bg-yellow-500 px-6" > No </button> </div> </div> </div> )} </div> )} </label> </div> ))} </div> </> );
Key Improvements
- No more DOM manipulation: All UI changes are controlled by React state, so your UI and state will always stay in sync.
- Per-player popup control: The state tracks exactly which player's popup is open, so clicking "Kick" on one player won't affect others.
- Correct
onClickhandling: Using arrow functions (onClick={() => togglePopup(player.username)}) ensures the function only runs when the button is clicked. - Cleaner logic: The popup is rendered directly in the component tree, making it easier to maintain and debug.
内容的提问来源于stack exchange,提问作者Prasesh Maharjan

