Reputation: 23
I'm new to ReactJs.
I made a background-color wrapper, to wrap my child component's color as background-color. Yes it's a bizarre edge-case and I wasn't able to find an alternative, but regardless;
When using the Wrapper in a page, everything functions as expected: the children's color is set to the parents. However when using it inside a Suspense's fallback, the code which actually fetches and update the color, never gets called.
backgroundcolor.tsx
"use client";
import {
Children,
cloneElement,
Fragment,
isValidElement,
ReactElement,
ReactNode,
useEffect,
useState,
} from "react";
export function BackgroundColorWrapper({
children,
}: Readonly<{
children: ReactNode;
}>) {
const [parentBgColor, setParentBgColor] = useState<string | null>(null);
const [isVisible, setIsVisible] = useState(false);
const childRef = (node: HTMLElement | null) => {
console.log("BackgroundColorWrapper childRef");
if (node) {
let currentElement = node.parentElement;
while (currentElement) {
const computedStyle = window.getComputedStyle(currentElement);
const backgroundColor = computedStyle.backgroundColor;
if (
backgroundColor !== "transparent" &&
backgroundColor !== "rgba(0, 0, 0, 0)" &&
backgroundColor !== "inherit"
) {
setParentBgColor(backgroundColor);
break;
}
currentElement = currentElement.parentElement;
}
if (!currentElement) {
setParentBgColor("inherit");
}
}
};
useEffect(() => {
console.log("BackgroundColorWrapper useEffect");
if (parentBgColor !== null) {
const timer = setTimeout(() => {
setIsVisible(true);
}, 50);
return () => clearTimeout(timer);
}
}, [parentBgColor]);
return (
<Fragment>
{Children.map(children, (child) => {
if (!isValidElement(child)) {
return child;
}
const test = cloneElement(child as ReactElement, {
ref: childRef,
style: {
...child.props.style,
visibility: parentBgColor === null ? "hidden" : "visible",
color: parentBgColor,
opacity: isVisible ? 1 : 0,
transition: "opacity 0.5s ease",
},
});
console.log("child", child);
return test;
})}
</Fragment>
);
}
export default BackgroundColorWrapper;
Nor, console.log("BackgroundColorWrapper childRef"); or console.log("BackgroundColorWrapper useEffect"); ever get called when inside a fallback.
And just to clarify:
// This works fine
return (
<BackgroundColorWrapper>
// xyz
</BackgroundColorWrapper>
);
// This does not
return (
<Suspense
fallback={
<BackgroundColorWrapper>
// xyz
</BackgroundColorWrapper>
}
></Suspense>
);
As requested MRE:
import BackgroundColorWrapper from "@/components/wrappers/backgroundcolor";
import { Suspense } from "react";
export default async function ExamplePage() {
return (
<main
style={{
display: "flex",
minHeight: "100vh",
alignItems: "center",
justifyContent: "center",
gap: "2rem",
backgroundColor: "white",
padding: "2rem",
}}
>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
borderRadius: "0.5rem",
border: "1px solid #e5e7eb",
padding: "1.5rem",
boxShadow: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
transition: "box-shadow 0.2s",
}}
>
<h2
style={{
marginBottom: "1rem",
fontSize: "1.125rem",
fontWeight: "600",
color: "#374151",
}}
>
Direct
</h2>
<div
style={{
height: "4rem",
width: "4rem",
}}
>
<LoadingUI />
</div>
</div>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
borderRadius: "0.5rem",
border: "1px solid #e5e7eb",
padding: "1.5rem",
boxShadow: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
transition: "box-shadow 0.2s",
}}
>
<h2
style={{
marginBottom: "1rem",
fontSize: "1.125rem",
fontWeight: "600",
color: "#374151",
}}
>
Suspense
</h2>
<div
style={{
height: "4rem",
width: "4rem",
}}
>
<Suspense fallback={<LoadingUI />}>
<ExampleProcessor />
</Suspense>
</div>
</div>
</main>
);
}
function SimpleCircle() {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50" fill="black" />
<circle cx="50" cy="50" r="25" fill="currentColor" />
</svg>
);
}
function LoadingUI() {
return (
<div>
<BackgroundColorWrapper>
<SimpleCircle />
</BackgroundColorWrapper>
</div>
);
}
async function ExampleProcessor() {
// wait 10 seconds
await new Promise((resolve) => setTimeout(resolve, 10000));
// Return the example component
return (
<h4
style={{
marginBottom: "1rem",
fontSize: "1.125rem",
fontWeight: "600",
color: "#374151",
}}
>
DONE
</h4>
);
}
Upvotes: 1
Views: 27