Skip to content

Commit

Permalink
Add server/client distinction; use growthbook-react
Browse files Browse the repository at this point in the history
  • Loading branch information
bttf committed Apr 22, 2024
1 parent 53b3d4c commit d838cea
Show file tree
Hide file tree
Showing 11 changed files with 63 additions and 55 deletions.
24 changes: 24 additions & 0 deletions next-js/src/app/client/ClientComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"use client";
import { useFeatureIsOn, useFeatureValue } from "@growthbook/growthbook-react";
export default function ClientComponent() {
const feature1Enabled = useFeatureIsOn("feature1");
const feature2Value = useFeatureValue("feature2", "fallback");
return (
<div>
<div className="text-4xl my-4">Client Component</div>
<p className="my-2">
This component renders client-side. The page initially delivered to the
client will not have FF values loaded, which can result in a
&apos;flicker&apos; when values are loaded asynchronously client-side.
</p>
<ul>
<li>
feature1: <strong>{feature1Enabled ? "ON" : "OFF"}</strong>
</li>
<li>
feature2: <strong>{feature2Value}</strong>
</li>
</ul>
</div>
);
}
60 changes: 17 additions & 43 deletions next-js/src/app/client/page.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,28 @@
"use client";
import { useEffect, useState } from "react";
import { useEffect } from "react";
import { GrowthBookProvider } from "@growthbook/growthbook-react";
import Cookies from "js-cookie";
import gb from "@/lib/growthbook";
import gb from "@/lib/growthbook/client";
import ClientComponent from "./ClientComponent";

export default function ClientPage() {
const [isLoading, setIsLoading] = useState(false);

useEffect(() => {
const load = async () => {
await gb.loadFeatures({ timeout: 1000 });

const userId = Cookies.get("gb-next-example-userId");

gb.setAttributes({
id: userId,
});
try {
await gb.loadFeatures();
const userId = Cookies.get("gb-next-example-userId");
gb.setAttributes({
id: userId,
});
} catch (e) {
console.error(e);
}
};
try {
setIsLoading(true);
load();
} catch (e) {
console.error(e);
} finally {
setIsLoading(false);
}
load();
}, []);

const feature1Enabled = gb.isOn("feature1");
const feature2Value = gb.getFeatureValue("feature2", "fallback");

return (
<div>
<div className="text-4xl my-4">Client Component</div>
<p className="my-2">
This component renders client-side. The page initially delivered to the
client will not have FF values loaded, which can result in a 'flicker'
when values are loaded asynchronously client-side.
</p>
{isLoading ? (
<div className="text-2xl animate-bounce">Loading...</div>
) : (
<ul>
<li>
feature1: <strong>{feature1Enabled ? "ON" : "OFF"}</strong>
</li>
<li>
feature2: <strong>{feature2Value}</strong>
</li>
</ul>
)}
</div>
<GrowthBookProvider growthbook={gb}>
<ClientComponent />
</GrowthBookProvider>
);
}
2 changes: 1 addition & 1 deletion next-js/src/app/server/combo/ClientComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";
import Cookies from "js-cookie";
import gb from "@/lib/growthbook";
import gb from "@/lib/growthbook/client";
import { FeatureDefinition } from "@growthbook/growthbook";

export default function ClientComponent({
Expand Down
11 changes: 5 additions & 6 deletions next-js/src/app/server/combo/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import gb from "@/lib/growthbook";
import gb from "@/lib/growthbook/server";
import { cookies } from "next/headers";
import ClientComponent from "./ClientComponent";

Expand All @@ -18,16 +18,15 @@ export default async function ServerDynamic() {
<div>
<div className="text-4xl my-4">Server / Client Component Combination</div>
<p className="my-2">
This server component fetches feature flags from GB server-side, then
passes the features object to a client-side component. The object passed
needs to be{" "}
This server component fetches feature flags from GB, then passes the
features object to a client-side component. Since the object passed is{" "}
<a
href="https://react.dev/reference/react/use-server#serializable-parameters-and-return-values"
target="_blank"
>
serializable
</a>{" "}
for this to work.
</a>
, we can avoid redundantly fetching the features object client-side.
</p>

<ClientComponent gbFeatures={features} />
Expand Down
2 changes: 1 addition & 1 deletion next-js/src/app/server/dynamic/ClientComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import { useEffect, useState } from "react";
import Cookies from "js-cookie";
import gb from "@/lib/growthbook";
import gb from "@/lib/growthbook/client";

export default function ClientPage() {
const [isLoading, setIsLoading] = useState(false);
Expand Down
2 changes: 1 addition & 1 deletion next-js/src/app/server/dynamic/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import gb from "@/lib/growthbook";
import gb from "@/lib/growthbook/server";
import { cookies } from "next/headers";

export default async function ServerDynamic() {
Expand Down
2 changes: 1 addition & 1 deletion next-js/src/app/server/static/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import gb from "@/lib/growthbook";
import gb from "@/lib/growthbook/server";
export default async function ServerStatic() {
await gb.loadFeatures({ timeout: 1000 });
gb.setAttributes({
Expand Down
2 changes: 1 addition & 1 deletion next-js/src/app/server/streaming/AsyncComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import gb from "@/lib/growthbook";
import gb from "@/lib/growthbook/server";
import { cookies } from "next/headers";

export default async function AsyncComponent() {
Expand Down
11 changes: 11 additions & 0 deletions next-js/src/lib/growthbook/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { GrowthBook } from "@growthbook/growthbook";

const growthbook = new GrowthBook({
apiHost: process.env.NEXT_PUBLIC_GROWTHBOOK_API_HOST,
clientKey: process.env.NEXT_PUBLIC_GROWTHBOOK_CLIENT_KEY,
decryptionKey: process.env.NEXT_PUBLIC_GROWTHBOOK_DECRYPTION_KEY,
// client-side features enabled
subscribeToChanges: true,
});

export default growthbook;
File renamed without changes.
2 changes: 1 addition & 1 deletion next-js/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { cookies } from "next/headers";
import gb from "@/lib/growthbook";
import gb from "@/lib/growthbook/server";

export async function middleware(request: NextRequest) {
await gb.loadFeatures({ timeout: 1000 });
Expand Down

0 comments on commit d838cea

Please sign in to comment.