From ea14d022a7e97dbb8f7a1633e0a25fa744ee28f5 Mon Sep 17 00:00:00 2001 From: Artur Uliashev Date: Sun, 15 Sep 2024 19:08:33 +0300 Subject: [PATCH 1/3] removed scope from config; added pageClientStarted --- .gitignore | 2 ++ pages/example/@id/+Page.tsx | 6 ++++++ pages/example/@id/+pageClientStarted.ts | 3 +++ pages/example/@id/ClientComponent.tsx | 12 ++++++++++++ pages/example/@id/model.ts | 8 ++++++++ renderer/+config.ts | 3 +++ renderer/+onBeforeRender.ts | 1 - renderer/+onBeforeRenderClient.ts | 20 +++++++++++--------- renderer/types.ts | 4 +--- 9 files changed, 46 insertions(+), 13 deletions(-) create mode 100644 pages/example/@id/+pageClientStarted.ts create mode 100644 pages/example/@id/ClientComponent.tsx diff --git a/.gitignore b/.gitignore index f9e25c5..6c8a02d 100644 --- a/.gitignore +++ b/.gitignore @@ -193,3 +193,5 @@ dist # End of https://www.toptal.com/developers/gitignore/api/node,linux,macos dist + +.idea diff --git a/pages/example/@id/+Page.tsx b/pages/example/@id/+Page.tsx index dbe0919..563f25c 100644 --- a/pages/example/@id/+Page.tsx +++ b/pages/example/@id/+Page.tsx @@ -1,8 +1,11 @@ import { useUnit } from "effector-react"; +import { clientOnly } from "vike-react/clientOnly"; import { Link } from "~/shared/routing"; import { $id } from "./model"; +const Component = clientOnly(() => import("./ClientComponent")); + export function Page() { const id = useUnit($id); @@ -10,6 +13,9 @@ export function Page() {

Example

Read parameter from route: {id}

+

+ Client id: +

Go home
); diff --git a/pages/example/@id/+pageClientStarted.ts b/pages/example/@id/+pageClientStarted.ts new file mode 100644 index 0000000..a1c27cc --- /dev/null +++ b/pages/example/@id/+pageClientStarted.ts @@ -0,0 +1,3 @@ +import { createEvent } from "effector"; + +export const pageClientStarted = createEvent(); diff --git a/pages/example/@id/ClientComponent.tsx b/pages/example/@id/ClientComponent.tsx new file mode 100644 index 0000000..c422b92 --- /dev/null +++ b/pages/example/@id/ClientComponent.tsx @@ -0,0 +1,12 @@ +import { useUnit } from "effector-react"; + +import { $clientId } from "./model"; + +// Since $clientId changes during client rendering to avoid hydration error we need to make consumer components only client +const ClientComponent = () => { + const clientId = useUnit($clientId); + + return <>{clientId}; +}; + +export default ClientComponent; diff --git a/pages/example/@id/model.ts b/pages/example/@id/model.ts index ab6ce07..29f4904 100644 --- a/pages/example/@id/model.ts +++ b/pages/example/@id/model.ts @@ -1,9 +1,11 @@ import { createStore, sample } from "effector"; import { redirectTo } from "~/shared/routing"; +import { pageClientStarted } from "./+pageClientStarted"; import { pageStarted } from "./+pageStarted"; export const $id = createStore(""); +export const $clientId = createStore(0); const dataInitialized = sample({ clock: pageStarted, @@ -21,3 +23,9 @@ sample({ fn: ({ sampleData: { id } }) => id, target: $id, }); + +sample({ + clock: pageClientStarted, + fn: () => 1, + target: $clientId, +}); diff --git a/renderer/+config.ts b/renderer/+config.ts index 2d0766a..182ce03 100644 --- a/renderer/+config.ts +++ b/renderer/+config.ts @@ -14,6 +14,9 @@ export default { pageStarted: { env: { client: true, server: true }, }, + pageClientStarted: { + env: { client: true, server: false }, + }, // https://effector.dev/en/api/effector/scope/ scope: { env: { client: true, server: true }, diff --git a/renderer/+onBeforeRender.ts b/renderer/+onBeforeRender.ts index 47dd5c4..8ba8a5a 100644 --- a/renderer/+onBeforeRender.ts +++ b/renderer/+onBeforeRender.ts @@ -28,7 +28,6 @@ export const onBeforeRender: OnBeforeRenderAsync = async (pageContext) => { return { pageContext: { - scope, // https://effector.dev/en/api/effector/serialize scopeValues: serialize(scope), }, diff --git a/renderer/+onBeforeRenderClient.ts b/renderer/+onBeforeRenderClient.ts index 292bcd7..ea14882 100644 --- a/renderer/+onBeforeRenderClient.ts +++ b/renderer/+onBeforeRenderClient.ts @@ -1,14 +1,16 @@ -import { fork } from "effector"; +import { allSettled, fork, serialize } from "effector"; // https://vike.dev/onBeforeRenderClient -export function onBeforeRenderClient(pageContext: Vike.PageContext) { +export async function onBeforeRenderClient(pageContext: Vike.PageContext) { // https://vike.dev/pageContext - if (!("scope" in pageContext)) { - return { - pageContext: { - // https://effector.dev/en/api/effector/fork/ - scope: fork({ values: pageContext.scopeValues }), - }, - }; + + const scope = fork({ values: pageContext.scopeValues }); + + const pageClientStarted = pageContext.config.pageClientStarted; + + if (pageClientStarted) { + await allSettled(pageClientStarted, { scope }); } + + pageContext.scopeValues = serialize(scope); } diff --git a/renderer/types.ts b/renderer/types.ts index b3a7bd9..7ebf70a 100644 --- a/renderer/types.ts +++ b/renderer/types.ts @@ -6,10 +6,8 @@ declare global { interface PageContext { config: { pageStarted?: EventCallable<{ params: Record; data: unknown }>; + pageClientStarted?: EventCallable; }; - - // https://effector.dev/en/api/effector/scope/ - scope?: Scope; scopeValues?: Record; } } From 1d8c7a33aa4ba76b381ec33085ec4fc5561abdb6 Mon Sep 17 00:00:00 2001 From: Artur Uliashev Date: Sun, 15 Sep 2024 19:19:22 +0300 Subject: [PATCH 2/3] removed 'Scope' type import --- renderer/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderer/types.ts b/renderer/types.ts index 7ebf70a..22f7919 100644 --- a/renderer/types.ts +++ b/renderer/types.ts @@ -1,4 +1,4 @@ -import type { EventCallable, Scope } from "effector"; +import type { EventCallable } from "effector"; // https://vike.dev/pageContext#typescript declare global { From 0247990afd7d8da01d6fb964cd1c061e3f52ac05 Mon Sep 17 00:00:00 2001 From: Artur Uliashev Date: Tue, 17 Sep 2024 13:24:54 +0300 Subject: [PATCH 3/3] fixed scopeValues --- package.json | 1 + pages/+Wrapper.tsx | 33 +++++++++++++++++++++++---- pages/example/@id/+Page.tsx | 11 +++++---- pages/example/@id/ClientComponent.tsx | 12 ---------- pnpm-lock.yaml | 16 +++++++++++++ renderer/+config.ts | 4 ---- renderer/+onBeforeRenderClient.ts | 5 ++-- 7 files changed, 54 insertions(+), 28 deletions(-) delete mode 100644 pages/example/@id/ClientComponent.tsx diff --git a/package.json b/package.json index 8bf2345..9de8241 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "node": ">=18.6.0" }, "dependencies": { + "@effector/next": "^0.7.0", "@effector/reflect": "^9.2.0", "@fastify/accepts": "^4.3.0", "@fastify/compress": "^7.0.3", diff --git a/pages/+Wrapper.tsx b/pages/+Wrapper.tsx index 412c21f..8c4c672 100644 --- a/pages/+Wrapper.tsx +++ b/pages/+Wrapper.tsx @@ -1,11 +1,36 @@ import type React from "react"; +import { useEffect, useRef } from "react"; -import { fork } from "effector"; -import { Provider } from "effector-react"; +import { EffectorNext } from "@effector/next"; +import { createEvent } from "effector"; +import { useUnit } from "effector-react"; import { usePageContext } from "vike-react/usePageContext"; +const noop = createEvent(); + +const Inner = () => { + const { config } = usePageContext(); + const clientStartedRef = useRef(false); + const onClientStarted = useUnit(config.pageClientStarted ?? noop); + + useEffect(() => { + if (!clientStartedRef.current && "pageClientStarted" in config) { + onClientStarted(); + clientStartedRef.current = true; + } + }, []); + + return <>; +}; + export default function WrapperEffector({ children }: { children: React.ReactNode }) { - const { scopeValues } = usePageContext(); + const pageContext = usePageContext(); + const { scopeValues } = pageContext; - return {children}; + return ( + + + {children} + + ); } diff --git a/pages/example/@id/+Page.tsx b/pages/example/@id/+Page.tsx index 563f25c..e7edbd6 100644 --- a/pages/example/@id/+Page.tsx +++ b/pages/example/@id/+Page.tsx @@ -1,20 +1,21 @@ import { useUnit } from "effector-react"; -import { clientOnly } from "vike-react/clientOnly"; import { Link } from "~/shared/routing"; -import { $id } from "./model"; - -const Component = clientOnly(() => import("./ClientComponent")); +import { $random } from "../../index/model"; +import { $clientId, $id } from "./model"; export function Page() { const id = useUnit($id); + const clientId = useUnit($clientId); + const random = useUnit($random); return (

Example

Read parameter from route: {id}

- Client id: + Client id: {clientId} + random: {random}

Go home
diff --git a/pages/example/@id/ClientComponent.tsx b/pages/example/@id/ClientComponent.tsx deleted file mode 100644 index c422b92..0000000 --- a/pages/example/@id/ClientComponent.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { useUnit } from "effector-react"; - -import { $clientId } from "./model"; - -// Since $clientId changes during client rendering to avoid hydration error we need to make consumer components only client -const ClientComponent = () => { - const clientId = useUnit($clientId); - - return <>{clientId}; -}; - -export default ClientComponent; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 760f7dd..e8ddd3b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@effector/next': + specifier: ^0.7.0 + version: 0.7.0(effector-react@23.2.1(effector@23.2.2)(react@18.3.1))(effector@23.2.2)(react@18.3.1) '@effector/reflect': specifier: ^9.2.0 version: 9.2.0(effector-react@23.2.1(effector@23.2.2)(react@18.3.1))(effector@23.2.2)(react@18.3.1) @@ -267,6 +270,13 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} + '@effector/next@0.7.0': + resolution: {integrity: sha512-8KrtdGr/mYboIM5rq5r39j2gX9lXWu/AC/+Iyp882Y0G2WO6cr8opamdOlZaVaNdJYBJQ6IyuBslhmoI2TBAOw==} + peerDependencies: + effector: ^22.8.6 || ^23.0.0 + effector-react: ^22.5.4 || ^23.0.0 + react: ^18.2.0 + '@effector/reflect@9.2.0': resolution: {integrity: sha512-jumgC1Ztl28gRmhLei2TX3bF0p1sD/LhAWHWggbmiNcZnpX4K6odTITrFLSu4wKkyP+AX9QpF/wiqLi0njDP1A==} peerDependencies: @@ -2088,6 +2098,12 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 + '@effector/next@0.7.0(effector-react@23.2.1(effector@23.2.2)(react@18.3.1))(effector@23.2.2)(react@18.3.1)': + dependencies: + effector: 23.2.2 + effector-react: 23.2.1(effector@23.2.2)(react@18.3.1) + react: 18.3.1 + '@effector/reflect@9.2.0(effector-react@23.2.1(effector@23.2.2)(react@18.3.1))(effector@23.2.2)(react@18.3.1)': dependencies: effector: 23.2.2 diff --git a/renderer/+config.ts b/renderer/+config.ts index 182ce03..f886cd8 100644 --- a/renderer/+config.ts +++ b/renderer/+config.ts @@ -17,10 +17,6 @@ export default { pageClientStarted: { env: { client: true, server: false }, }, - // https://effector.dev/en/api/effector/scope/ - scope: { - env: { client: true, server: true }, - }, }, // https://vike.dev/extends diff --git a/renderer/+onBeforeRenderClient.ts b/renderer/+onBeforeRenderClient.ts index ea14882..7c30afc 100644 --- a/renderer/+onBeforeRenderClient.ts +++ b/renderer/+onBeforeRenderClient.ts @@ -8,9 +8,8 @@ export async function onBeforeRenderClient(pageContext: Vike.PageContext) { const pageClientStarted = pageContext.config.pageClientStarted; - if (pageClientStarted) { + if (pageClientStarted && !pageContext.isHydration) { await allSettled(pageClientStarted, { scope }); + pageContext.scopeValues = serialize(scope); } - - pageContext.scopeValues = serialize(scope); }