I’m trying to pass the Square element’s innerHTML to the onClick function. I have also tried to pass in just i
but it always is equal to 100. Is there a way to either pass i
when it’s equal to the same value that goes into the Square or is there a way to pass the innerHTML to the function. Currently, this code generates the error:
[TS: 2532]this is possibly undefined
I’m making a grid of 100 squares, each one is a button, and each one should have it’s own ID/number from 1-100 to identify them.
This is what I have currently: Grid of 100 squares arranged in 10×10 formation
export const Square = (props:any) =>{ i += 1; if(i > 100) { i = 1; } return( <DefaultButton styles={factionMapButton} onClick={()=>onSquareClick(this.innerHTML,props.onClick)}>{i}</DefaultButton> ); } const onSquareClick = (number:any,isOpen:any) => { console.log(number); const panelContent = document.getElementById("panelContent"); if(panelContent !== null) { panelContent.innerHTML = number; } isOpen(); }
Advertisement
Answer
You have quite a few problems.
- You should do your best to avoid
any
in TypeScript, especially not liberally – that defeats the whole purpose of type-checking. If you’re trying to fix type problems, you should start by also typing everything properly. - Arrow functions do not have their
this
altered by the calling context. If there’s no enclosing full-fledgedfunction
, thethis
in an arrow function will be the global object orundefined
, both of which are useless to you. Either use afunction
to capture thethis
, or, even better, use the click event’s currentTarget to get a reference to the clicked button. - The
.innerHTML
of an element returns a string, not an element. If it contains a string that can be coerced to a number, explicitly coerce it to a number instead. (If the HTML content is only the string that can be coerced to the number, you should use.textContent
instead – only use.innerHTML
when deliberately setting or retrieving HTML markup, not plain text) - A better approach would be to pass down the
i
toonSquareClick
instead of using DOM manipulation – using the closure is much easier
let i = 1; export const Square = ({ onClick }: { onClick: () => void }) => { i += 1; if (i > 100) { i = 1; } return ( <DefaultButton styles={factionMapButton} onClick={(e) => { onSquareClick(i, onClick); }}>{i}</DefaultButton> ); }; const onSquareClick = (number: number, isOpen: () => void) => { const panelContent = document.getElementById('panelContent'); if (panelContent !== null) { panelContent.innerHTML = String(number); } isOpen(); };
- If you’re using React, you should not be using vanilla DOM manipulation like
panelContent.innerHTML = number;
– instead, set React state that the view uses to determine what should exist in that element. Something like
// Parent component: const [panelContentText, setPanelContentText] = useState(''); // expand as needed for the other components in your app... return <div id="panelContent">{panelContentText}</div> <Square setPanelContentText={setPanelContentText} /* and other props */ /> // ... // Then call the setPanelContentText prop in onSquareClick const onSquareClick = (number: number, isOpen: () => void, setPanelContentText: React.Dispatch<React.SetStateAction<string>>) => { setPanelContentText(String(number)); isOpen(); };
I’d recommend looking into an introductory React tutorial, it looks like you might benefit from learning the process within React’s ecosystem, rather than trying to mishmash your understanding of vanilla JS’s DOM with React.