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/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 dbe0919..e7edbd6 100644 --- a/pages/example/@id/+Page.tsx +++ b/pages/example/@id/+Page.tsx @@ -1,15 +1,22 @@ import { useUnit } from "effector-react"; import { Link } from "~/shared/routing"; -import { $id } from "./model"; +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: {clientId} + random: {random} +

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/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/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 2d0766a..f886cd8 100644 --- a/renderer/+config.ts +++ b/renderer/+config.ts @@ -14,9 +14,8 @@ export default { pageStarted: { env: { client: true, server: true }, }, - // https://effector.dev/en/api/effector/scope/ - scope: { - env: { client: true, server: true }, + pageClientStarted: { + env: { client: true, server: false }, }, }, 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..7c30afc 100644 --- a/renderer/+onBeforeRenderClient.ts +++ b/renderer/+onBeforeRenderClient.ts @@ -1,14 +1,15 @@ -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 && !pageContext.isHydration) { + await allSettled(pageClientStarted, { scope }); + pageContext.scopeValues = serialize(scope); } } diff --git a/renderer/types.ts b/renderer/types.ts index b3a7bd9..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 { @@ -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; } }