React escapes anything interpolated into JSX, which removes the most common form of this bug by default. <div>{userInput}</div> renders a script tag as text. That's why XSS in React apps is comparatively rare and why, when it happens, it's in one of a small number of places.
dangerouslySetInnerHTML
The name is a warning and it gets used anyway, usually to render rich text from a CMS or user content.
If post.body can contain anything a user wrote, that executes. Sanitise it:
Sanitise at render rather than on save. Sanitising once on input means any change to your sanitiser rules doesn't apply to data already stored, and you have no way of knowing what got through under the old rules.
User-controlled URLs
React escapes text, not protocols.
A value of javascript:alert(document.cookie) executes on click. Same applies to src on iframes and to anything accepting a URL.
Refs and direct DOM access
Anything reaching past React into the DOM leaves React's escaping behind.
Use textContent for text, or sanitise if the markup is genuinely needed.
Third-party libraries that manipulate the DOM directly, chart tooltips, rich text editors, older jQuery plugins wrapped in components. Deserve the same suspicion.
Server-rendered state
If you serialise state into the HTML document, you've left React's protection entirely.
JSON.stringify doesn't escape </script>, so user data containing that string closes the tag early and everything after it is markup. Escape the output or use a library built for the job.
Defence in depth
A Content Security Policy limits what an XSS payload can do even if one lands. It isn't a substitute for the fixes above, but it's the difference between a bug and an incident.
Auditing what you already have
Scanners handle dangerouslySetInnerHTML reasonably well because it's a distinctive call with a traceable input. They do less well on the URL case, where the vulnerable code is an ordinary attribute assignment, and worse on third-party DOM manipulation.
Grep for dangerouslySetInnerHTML, innerHTML and .href = as a first pass. It's crude and it will find most of what's there.