P粉6424362822023-08-16 10:19:47
You are mixing DOM created using React and DOM manipulated using pure Javascript. You can't do this, it will just break the DOM/VDOM relationship.
If you want to avoid moving the entire component to work the way React does, then just use Ref on an empty element, the benefit is that you get the benefits of component mounting and unmounting.
Then you can pretty much copy-paste the Javascript code and just put it all inside useEffect
.
For example:
Below I have created two buttons, one controlled entirely by React and the other by JS, when you click on it it will just toggle the pink-button
class.
I've also placed a checkbox to flip the order of the buttons to cause a re-render to show that the JS version's state is not corrupted, make sure to use the key
attribute when doing this to let React know it's the same components. The main point here is to show that React will now no longer interfere with this button, it is now entirely your responsibility. Hope this helps you understand how to mix React with pure JS.
const domContainer = document.querySelector('#mount'); const root = ReactDOM.createRoot(domContainer); const {createRef, useEffect, useState} = React; function JSButton() { const btnRef = createRef(); useEffect(() => { //do everything now from inside this useEffect const btn = btnRef.current; btn.innerText = 'This is a Javascript Button'; const doClick = () => btn.classList.toggle('pink-button'); btn.addEventListener('click', doClick); return () => { //we can also do any clean-up here btn.removeEventListner('click', doClick); } }, []); //only render the button component, //don't add any props, classes etc. //our JS will be handling it all. return <button ref={btnRef}/>; } function ReactButton() { const [pink, setPink] = useState(false); return <button onClick={() => setPink(p => !p)} className={pink ? 'pink-button' : ''}> React Button </button>; } function Test() { const [jsFirst, setJsFirst] = useState(false); return <React.Fragment> <input type="checkbox" checked={jsFirst} onChange={(e) => { setJsFirst(e.target.checked); }} />Js First {jsFirst ? <React.Fragment> <JSButton key={'js'}/> <ReactButton key={'r'}/> </React.Fragment> : <React.Fragment> <ReactButton key={'r'}/> <JSButton key={'js'}/> </React.Fragment> } </React.Fragment> } root.render(<Test/>);
.pink-button { background-color: pink; } button { display: block; width: 200px; margin: 1rem; }
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script> <div id="mount"></div>