Skip to content

Commit b80e308

Browse files
committed
refactor react in svelte to account for multiple re-renders
1 parent d6ed787 commit b80e308

File tree

1 file changed

+46
-28
lines changed

1 file changed

+46
-28
lines changed

src/react/browser.tsx

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { PropsWithChildren } from 'react';
22
import {
33
attr,
44
children,
@@ -59,7 +59,7 @@ function create_fragment(ctx) {
5959
function instance($$self, $$props, $$invalidate) {
6060
let { id } = $$props;
6161

62-
$$self.$$set = $$props => {
62+
$$self.$$set = ($$props) => {
6363
if ('id' in $$props) $$invalidate(0, (id = $$props.id));
6464
};
6565

@@ -114,55 +114,73 @@ function createSlot(id) {
114114
}
115115

116116
export function createReactComponent(name: string, SvelteComponent$$: any) {
117-
let ReactComponent$$ = function ReactComponent(props) {
118-
const rootRef = React.useRef<HTMLDivElement>(null);
117+
let ReactComponent$$ = function ReactComponent(
118+
props: PropsWithChildren<unknown>,
119+
) {
119120
const svelteComponentRef = React.useRef<any>();
121+
const [svelteMountTarget, setSvelteMountTarget] =
122+
React.useState<HTMLElement | null>(null);
123+
const onRefChange = React.useCallback(setSvelteMountTarget, [
124+
setSvelteMountTarget,
125+
]);
120126
const [mounted, setMounted] = React.useState(false);
121127
const id = React.useId();
128+
const slotId = `${id}-slot`;
122129
const events = getEvents(props);
123130
const { children, ...svelteProps } = props;
124131

125132
React.useEffect(() => {
126-
const defaultSlot = createSlot(id);
127-
let component = new SvelteComponent$$({
128-
target: rootRef.current,
129-
props: {
130-
...svelteProps,
131-
$$slots: {
132-
default: [defaultSlot],
133+
if (svelteMountTarget) {
134+
const defaultSlot = createSlot(id);
135+
136+
svelteComponentRef.current = new SvelteComponent$$({
137+
target: svelteMountTarget,
138+
props: {
139+
...svelteProps,
140+
$$slots: {
141+
default: [defaultSlot],
142+
},
143+
$$scope: {},
133144
},
134-
$$scope: {},
135-
},
136-
hydrate: true,
137-
});
138-
setMounted(true);
139-
svelteComponentRef.current = component;
145+
hydrate: true,
146+
});
140147

141-
for (const [event, handler] of events) {
142-
component.$on(event, handler);
143-
}
148+
setMounted(true);
144149

145-
return () => {
146-
component.$destroy();
147-
};
148-
}, []);
150+
for (const [event, handler] of events) {
151+
svelteComponentRef.current.$on(event, handler);
152+
}
153+
154+
return () => {
155+
svelteComponentRef.current.$destroy();
156+
setMounted(false);
157+
};
158+
}
159+
}, [svelteMountTarget]);
149160

150161
React.useEffect(() => {
151162
React.startTransition(() => {
152-
svelteComponentRef.current!.$set(props);
163+
svelteComponentRef.current?.$set(svelteProps);
153164
});
154-
}, [props]);
165+
}, [svelteProps]);
155166

156167
return (
157168
<>
158169
<div
159-
ref={rootRef}
170+
key={id}
171+
ref={onRefChange}
160172
style={{ display: 'contents' }}
161173
dangerouslySetInnerHTML={{ __html: '' }}
162174
/>
163175
{mounted &&
164176
children &&
165-
ReactDOM.createPortal(children, document.getElementById(id))}
177+
ReactDOM.createPortal(
178+
children,
179+
svelteComponentRef.current.$$.root.querySelector(
180+
`[id='${slotId}']`,
181+
),
182+
slotId,
183+
)}
166184
</>
167185
);
168186
};

0 commit comments

Comments
 (0)