I have a very basic page that I use to experiment with React Server Components in an existing Next.js app:
// src/app/empty/page.tsx
import { Footer } from "components/layout/footer/footer";
export default function EmptyPage() {
console.log("EmptyPage");
return (
<>
<div className="border-white-10% m-2 inline-block border p-2">
<h1>Empty Page</h1>
</div>
<Footer />
</>
);
}
I run next dev and visit http://localhost:3000/empty. The page appears, the footer is rendered and I see the following in server console:
./src/hooks/use-client-side-value.ts
Attempted import error: 'useEffect' is not exported from 'react' (imported as 'useEffect').
Import trace for requested module:
./src/hooks/use-client-side-value.ts
./src/components/layout/footer/social-buttons.tsx
./src/components/layout/footer/footer.tsx
EmptyPage
Each of footer.tsx, social-buttons.tsx and use-client-side-value.ts has "use client"; at the top, so it should be safe to use useEffect there, right? What am I doing wrong?
Full code of the components:
// footer.tsx
"use client";
import React from "react";
import SocialButtons from "./social-buttons";
interface FooterProps {
hasLargePadding?: boolean;
}
export const Footer = ({ hasLargePadding = true }: FooterProps) => {
return (
<div
className={`px-3 md:px-6 ${hasLargePadding && "lg:px-18"}`}
>
<SocialButtons />
</div>
);
};
// social-buttons.tsx
"use client";
import { useClientSideValue } from "hooks/use-client-side-value";
import { isAndroid, isIOS } from "react-device-detect";
import SocialButton from "./social-button";
const SocialButtons = () => {
const facebookSchemeUri = useClientSideValue(() => {
if (isIOS) return "fb://profile/112142838181341";
if (isAndroid) return "fb://page/112142838181341";
return undefined;
});
return (
<div className="flex">
<SocialButton
href="https://facebook.com/company"
schemeUri={facebookSchemeUri}
icon={<img src="/assets/icons/facebook.svg" alt="Facebook" />}
/>
<SocialButton
href="https://instagram.com/company"
icon={
<img
src="/assets/icons/instagram.svg"
alt="Instagram"
style={{ marginLeft: 12 }}
/>
}
/>
</div>
);
};
export default SocialButtons;
// hooks/use-client-side-value.ts
"use client";
import { useEffect, useState } from "react";
export function useClientSideValue<T>(callback: () => T) {
const [clientSideValue, setClientSideValue] = useState<undefined | T>();
useEffect(() => {
setClientSideValue(callback());
}, []);
return clientSideValue;
}