Skip to content

Commit bdf5336

Browse files
committed
Merge branch 'docs/v1'
2 parents 5aae60e + cf26493 commit bdf5336

File tree

7 files changed

+450
-71
lines changed

7 files changed

+450
-71
lines changed

README.md

Lines changed: 364 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,364 @@
1-
# effector/vike-react-template
1+
# Effector SSR Vike Application Template
2+
3+
This project is a template for quickly starting a Server-Side Rendering (SSR) Node.js application, combining a powerful set of vite, react, effector, and fastify.
4+
5+
## Features
6+
7+
- **File-system routing** implemented by Vike;
8+
- **Effector** to cover state management cases;
9+
- **React.js** to use the giant ecosystem of UI components;
10+
11+
## Used Tools
12+
13+
- **TypeScript**: v5 with [ts-node](https://npmjs.com/ts-node)
14+
- **React**: v18
15+
- **Vite**: v5 with [vite-plugin-svgr](https://npmjs.com/vite-plugin-svgr), [@vitejs/plugin-react](https://npmjs.com/@vitejs/plugin-react)
16+
- **Vike**: v0.4 with [vike-react](https://npmjs.com/vike-react) and few hooks in ./renderer
17+
- **Fastify**: v4 with [cookie](https://npmjs.com/cookie), [cors](https://npmjs.com/cors), [early-hints](https://npmjs.com/early-hints), [helmet](https://npmjs.com/helmet), [rate-limit](https://npmjs.com/rate-limit), more.
18+
- **Effector**: v23 with [patronum](https://npmjs.com/patronum), [reflect](https://npmjs.com/@effector/reflect), [factorio](https://npmjs.com/effector-factorio)
19+
- **Zod**: v3
20+
- **SVGR**: as [vite-plugin-svgr](https://npmjs.com/vite-plugin-svgr) v4
21+
- **Prettier**: v3 with [plugin-sort-imports](@trivago/prettier-plugin-sort-imports) by trivago
22+
23+
## Requirements
24+
25+
- Node.js 18.6+
26+
- corepack enabled (but can be used without)
27+
28+
## Use this template
29+
30+
0. Press "Use this template" to create a copy of repository.
31+
32+
1. Clone your repository:
33+
34+
```bash
35+
git clone https://github.com/effector/vike-react-template my-app
36+
# OR
37+
gh repo clone effector/vike-react-template my-app
38+
```
39+
40+
2. Navigate to the project directory:
41+
42+
```bash
43+
cd my-app
44+
```
45+
46+
3. Install package manager and dependencies:
47+
48+
```bash
49+
corepack enable
50+
corepack prepare
51+
pnpm install
52+
```
53+
54+
## Usage
55+
56+
1. Run in development mode:
57+
58+
```bash
59+
pnpm dev
60+
```
61+
62+
2. Build for production:
63+
64+
```bash
65+
pnpm build
66+
```
67+
68+
3. Run production version:
69+
70+
```bash
71+
pnpm start
72+
```
73+
74+
## Project Structure
75+
76+
Strongly recommend to carefully **review each file** in this repository, before using it in production.
77+
78+
This project inherites [Vike project structure](https://vike.dev/file-structure):
79+
80+
dist/
81+
pages/
82+
public/
83+
renderer/
84+
server/
85+
src/
86+
87+
- `dist` contains result of `pnpm build`, it is production build;
88+
- `pages` is a Vike's [filesystem routing](https://vike.dev/routing#filesystem-routing);
89+
- `public` is a [static files directory](https://vike.dev/static-directory#public);
90+
- `renderer` is a react + effector [integration hooks](https://vike.dev/file-structure#renderer);
91+
- `server` is a fastify server, builds with `tsc`, runs with `ts-node`;
92+
- `src` is a [FSD](https://feature-sliced.design) basis, with code imported into `pages`, and `renderer`;
93+
94+
### `pages/`
95+
96+
#### `pages/+Wrapper.tsx`
97+
98+
It is a data provider for logic uses effector.
99+
100+
#### `pages/+Layout.tsx`
101+
102+
To wrap up components with some layout use [`+Layout.tsx`](https://vike.dev/Layout).
103+
104+
Also, you can [nest multiple layouts](https://vike.dev/Layout#nested-layouts).
105+
106+
#### `pages/index/+pageStarted.ts`
107+
108+
This vike hook describes what event Vike should call on server when page logic can be started.
109+
110+
Usually looks like this:
111+
112+
```ts
113+
import { createPageStart } from "~/shared/init";
114+
115+
export const pageStarted = createPageStart();
116+
117+
// pageStarted has type:
118+
pageStarted: EventCallable<{
119+
params: Record<string, string>;
120+
data: void;
121+
}>;
122+
```
123+
124+
`params` looks like `{ id: "foo" }` for route `pages/example/@id` and pathname `/example/foo`.
125+
126+
| Url | Route | `params` |
127+
| ------------ | ----------------- | --------------- |
128+
| / | pages/index | `{}` |
129+
| /example/100 | pages/example/@id | `{ id: "100" }` |
130+
131+
#### `pages/index/+Page.tsx`
132+
133+
This is a page component. It can import `model.ts` and all from src using `~/` alias.
134+
135+
Use `export default` and named functions:
136+
137+
```tsx
138+
export default function PageHome() {
139+
return <h1>Hello World</h1>;
140+
}
141+
```
142+
143+
#### `pages/index/model.ts`
144+
145+
This is a logic file written in effector. It can import `+pageStarted.ts` and all from src using `~/` alias.
146+
147+
```ts
148+
import { createEffect, sample } from "effector";
149+
150+
import { pageStarted } from "./+pageStarted";
151+
152+
const helloFx = createEffect((name: string) => {
153+
console.info(`Hello ${name}`);
154+
});
155+
156+
sample({
157+
clock: pageStarted,
158+
fn: () => "World",
159+
target: helloFx,
160+
});
161+
```
162+
163+
When user opened http://localhost:3000, `pageStarted` fired, then sample with `clock: pageStarted` reacts and triggers `helloFx` with `"World"` argument.
164+
165+
In our dynamic and event driven kind of environment, this is the powerful way to describe logic.
166+
**Without needing to deal with React**, Hooks, Rerenders, StrictMode, Next.js, etc.
167+
168+
### `pages/example/@id`
169+
170+
Let's talk about data loading.
171+
172+
You can always use simple `createEffect` to load data in Browser, just react on user actions, not `pageStarted` nor `appStarted`.
173+
174+
Until, you read [Data Fetching article](https://vike.dev/data-fetching) from Vike.dev. It will works until [Client-Side Routing](https://vike.dev/client-routing).
175+
Vike has `+data.ts` hook to fetch data on client and server navigation.
176+
177+
> In case of refetch data using triggering some event on client side, or changing filters in user interface consider making client navigation with query parameters.
178+
179+
#### `pages/example/@id/+data.ts`
180+
181+
Declare your data fetcher. Name of the exported function must be `data`. Use this as starting point:
182+
183+
```ts
184+
import type { PageContextServer } from "vike/types";
185+
186+
export async function data(pageContext: PageContextServer) {
187+
const { routeParams } = pageContext;
188+
const { id } = routeParams;
189+
190+
// await api.someItems.getById(id)
191+
192+
return {
193+
sampleData: { id: id ?? "<empty>" },
194+
};
195+
}
196+
```
197+
198+
Consider placing all reusable API requests into `src/shared/api`.
199+
Using barrel file pattern is optional but very useful.
200+
201+
#### `pages/example/@id/+pageStarted.ts`
202+
203+
In case of data loading, hook pageStarted should be modified:
204+
205+
```ts
206+
import { createPageStart } from "~/shared/init";
207+
208+
import type { data } from "./+data";
209+
210+
export const pageStarted = createPageStart<Awaited<ReturnType<typeof data>>>();
211+
```
212+
213+
You need just bind resulting type of your `data` loader function. Vike passes result of `data()` call into `pageStarted` like `{ params: { id }, data }`.
214+
215+
#### `pages/example/@id/model.ts`
216+
217+
So, you can access data from `model` like this:
218+
219+
```ts
220+
import { createStore, sample } from "effector";
221+
222+
import { pageStarted } from "./+pageStarted";
223+
224+
sample({
225+
clock: pageStarted,
226+
fn: ({ data, params: { id } }) => data,
227+
target: insertHereAnyUnit,
228+
});
229+
// you can actually use `source, filter` in sample
230+
```
231+
232+
### `pages/_error`
233+
234+
Component created as `pages/_error/+Page.tsx` is used to show [error page](https://vike.dev/error-page).
235+
236+
### `renderer/`
237+
238+
Here described exact integration of vike, react, and effector.
239+
You may need to read this before changing:
240+
241+
- [Vike Hooks](https://vike.dev/hooks);
242+
- [Build Your Own Framework](https://vike.dev/build-your-own-framework) on top of Vike;
243+
- Don't be shy, read source files, it has links to documentation;
244+
245+
### `server/`
246+
247+
#### `server/config.ts`
248+
249+
Contains resolvers of configuration variables.
250+
251+
```ts
252+
export const CONFIG = {
253+
get SERVER_PORT() {
254+
return Number.parseInt(globalThis.process.env.SERVER_PORT ?? "3000", 10);
255+
},
256+
};
257+
```
258+
259+
> This is the only way it works on Cloudflare Workers. If you have a better solution please [Leave an issue](https://github.com/effector/vike-react-template/issues).
260+
261+
#### `server/directory-root.ts`
262+
263+
Declares project root directory, to resolve static assets from.
264+
265+
#### `server/index.ts`
266+
267+
Creates instance of `server/server`, handles signals SIGINT, SIGTERM.
268+
269+
#### `server/server.ts`
270+
271+
Creates fastify instance, configures plugins, read cookies, renders using vike/server.
272+
273+
You can modify any part of this and any other files. Please, explore documentation before.
274+
275+
#### `server/tsconfig.json`
276+
277+
Used here to describe different environment for files in this directory.
278+
279+
It builds for production with `tsc -p server`. It runs for development with `ts-node` ESM-loader.
280+
281+
There is no hot reload in `server/` directory. Restart manually after changing these files.
282+
283+
### `src/`
284+
285+
Consider using [Feature-Sliced Design](https://feature-sliced.design/) structuring this directory.
286+
287+
#### `src/vike.d.ts`
288+
289+
Handles [`pageContext` typings](https://vike.dev/pageContext#typescript).
290+
291+
#### `src/vite-env.d.ts`
292+
293+
Handles [Vite client types](https://vitejs.dev/guide/features.html#client-types).
294+
295+
## Customization
296+
297+
> What kind of customizations needs to be described? integration with supabase? [Leave an issue](https://github.com/effector/vike-react-template/issues).
298+
299+
### Use different Package Manager
300+
301+
First of all, delete `pnpm-lock.yaml`, dist, and node_modules:
302+
303+
```bash
304+
rm pnpm-lock.yaml
305+
rm -rf node_modules dist
306+
```
307+
308+
#### Node.js v18.x:
309+
310+
Set exact version into `packageManager` field of `package.json`:
311+
312+
```json
313+
// package.json
314+
{
315+
"packageManager": "[email protected]" // "[email protected]",
316+
}
317+
```
318+
319+
Save and navigate to your terminal into the project directory:
320+
321+
```bash
322+
# Enable corepack for your shell
323+
corepack enable
324+
325+
# Install package manager for your project
326+
corepack prepare
327+
328+
# Install dependencies
329+
npm install
330+
```
331+
332+
#### Node.js v20+:
333+
334+
Node.js v20 has different version of corepack, so we can use `corepack use`.
335+
336+
```bash
337+
# Enable corepack for your shell
338+
corepack enable
339+
340+
# Install package manager for your project
341+
corepack use npm@latest # yarn@3
342+
343+
# Install dependencies
344+
npm install
345+
346+
# Check packageManager field in package.json
347+
jq .packageManager package.json
348+
#> "[email protected]+sha256.1ee0e26fb669143425371ab8727fe4c5841640a2fd944863a8e8c28be966aca2"
349+
# It's OK
350+
```
351+
352+
## Supported OS and Node.js
353+
354+
This template has been tested on:
355+
356+
- macOS Sonoma 14.5, Node.js v20.10.0
357+
358+
## Contributing
359+
360+
We welcome contributions to the project! Please read our contribution guidelines before submitting a pull request.
361+
362+
## License
363+
364+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

0 commit comments

Comments
 (0)