11import React , { useEffect , useRef } from 'react' ;
2+ // @ts -ignore
3+ import ReactDOMClient from 'react-dom/client' ;
24import ReactDOM from 'react-dom' ;
35import { useState } from 'react' ;
46import { babelTransform } from './transform' ;
@@ -14,13 +16,51 @@ export function useCodePreview(props: CodePreviewProps) {
1416 const [ copied , setCopied ] = useState ( false ) ;
1517 const [ code , setCode ] = useState ( props . code || '' ) ;
1618
19+ /** 通过缓存的方式 解决 react v18 中 的报错 ***/
20+ // @ts -ignore
21+ const cachesRef = React . useRef ( new Map < string , ReactDOMClient . Root > ( [ ] ) ) ;
22+ const ReactDOMRender = ( _ReactDOM : typeof ReactDOMClient ) => {
23+ return {
24+ createRoot : ( id : string ) => {
25+ return {
26+ render : ( render : React . ReactChild | Iterable < React . ReactNode > ) => {
27+ const caches = cachesRef . current ;
28+ let root = caches . get ( id ) ;
29+ // 存在则不需要重新创建直接进行render操作
30+ if ( root ) {
31+ root . render ( render ) ;
32+ } else {
33+ // @ts -ignore
34+ root = _ReactDOM . createRoot ( document . getElementById ( id ) ! ) ;
35+ root . render ( render ) ;
36+ // 缓存,解决控制台报 ReactDOMClient.createRoot 问题
37+ caches . set ( id , root ) ;
38+ }
39+ cachesRef . current = caches ;
40+ } ,
41+ } ;
42+ } ,
43+ } ;
44+ } ;
45+ /** ------------------------ ***/
46+
1747 const executeCode = ( str : string ) => {
18- const { React : _React , ReactDOM : _ReactDOM , ...otherDeps } = props . dependencies || { } ;
48+ const {
49+ React : _React ,
50+ ReactDOM : _ReactDOM ,
51+ ReactDOMClient : _ReactDOMClient ,
52+ ...otherDeps
53+ } = props . dependencies || { } ;
54+ const V18ReactDOM = _ReactDOMClient || ReactDOMClient || _ReactDOM || ReactDOM ;
55+ // 判断是否是 react v18版本
56+ const isV18 = Reflect . has ( V18ReactDOM || { } , 'createRoot' ) ;
57+ const NewReactDOM = isV18 ? ReactDOMRender ( V18ReactDOM ) : V18ReactDOM ;
58+
1959 try {
2060 const deps = {
2161 React : _React || React ,
22- ReactDOM : _ReactDOM || ReactDOM ,
2362 ...otherDeps ,
63+ ReactDOM : NewReactDOM ,
2464 } as any ;
2565 // const args = ['context', 'React', 'ReactDOM', 'Component'];
2666 const args = [ ] ;
@@ -30,7 +70,17 @@ export function useCodePreview(props: CodePreviewProps) {
3070 args . push ( key ) ;
3171 argv . push ( deps [ key ] ) ;
3272 }
33- str = str . replace ( '_mount_' , `document.getElementById('${ playerId . current } ')` ) ;
73+
74+ if ( isV18 ) {
75+ // react < v18 中写法替换
76+ str = str . replace ( 'ReactDOM.render' , `ReactDOM.createRoot("${ playerId . current } ").render` ) ;
77+ // react v18 中写法替换
78+ str = str . replace ( `ReactDOMClient.createRoot(_mount_)` , `ReactDOM.createRoot("${ playerId . current } ")` ) ;
79+ str = str . replace ( '_mount_' , `` ) ;
80+ } else {
81+ str = str . replace ( '_mount_' , `document.getElementById('${ playerId . current } ')` ) ;
82+ }
83+
3484 const input = `${ str } ` ;
3585 const { code } = babelTransform ( input ) ;
3686 args . push ( code || '' ) ;
0 commit comments