function App() {
const [data, setData] = useState("some state");
return (
<ParentComponent>
<ComponentOne>
<ComponentTwo data={data} />
</ComponentOne>
</ParentComponent>
);
}
blog.logrocket.com/solving…act-apps
Короткие и полезные заметки про React
function App() {
const [data, setData] = useState("some state");
return (
<ParentComponent>
<ComponentOne>
<ComponentTwo data={data} />
</ComponentOne>
</ParentComponent>
);
}
blog.logrocket.com/solving…act-apps
// server/index.ts
import * as trpc from '@trpc/server';
import { z } from 'zod';
const appRouter = trpc
.router()
.query('getUser', {
input: (val: unknown) => {
if (typeof val === 'string') return val;
throw new Error(`Invalid input: ${typeof val}`);
},
async resolve(req) {
req.input; // string
return { id: req.input, name: 'Bilbo' };
},
})
.mutation('createUser', {
// validate input with Zod
input: z.object({ name: z.string().min(5) }),
async resolve(req) {
// use your ORM of choice
return await UserModel.create({
data: req.input,
});
},
});
export type AppRouter = typeof appRouter;
https://trpc.io/esbuild --bundle src/index.js --outfile=www/main.js --servedir=www
// next.config.js
module.exports = {
experimental: {
concurrentFeatures: true,
serverComponents: true,
},
}
Серверные компоненты могут быть полезны при загрузке большого количества данных. Все запросы будут выполняться на сервере, а на клиенте будет только один запрос на получение RSC, что позволяет избежать больших водопадов загрузки в браузере.
В серверных компонентах использование Suspense позволяет не ждать загрузки всех данных для ответа клиенту. Suspense создает “слоты” в возвращаемом JSON. При получении данных и рендере компонентов внутри Suspense, сервер стримит JSON новых компонентов клиенту, который вставляет их в нужные слоты.
Не стоит путать серверные компоненты и техники пререндера контента, такие как Server-side rendering (SSR) или Static site generation (SSG). RSC позволяет отрендерить на сервере только часть компонентов на странице, а не всю страницу целиком.
blog.logrocket.com/react-s…extjs-12
демка: https://next-news-rsc.vercel.sh/ (github)
const none = {};
function useLazyRef(init) {
const ref = useRef(none);
if (ref.current === none) {
ref.current = init();
}
return ref;
}
- Использовать useState. Хук можно использовать как реф если игнорировать функцию обновления. В этом случае, в хуке будет записана ссылка на один объект, и его изменение не будет вызывать ререндер.
const ref = useState(() => ({ current: init() }))[0];
- Создать кастомный хук с полями аксессорами (accessor properties). Если у объекта задать геттер и сеттер на поле current и внутри использовать реф, то получится правильная ленивая инициализация рефа. Объект будет проинициализирован только при доступе к полю current.
thoughtspile.github.io/2021/11…y-userefuseLoaderData. Основной способ передачи информации с клиента на сервер – формы. Для обработки форм в модуле экспортируется переменная action. Внутри функции action можно валидировать поля, обращаться к базе данных и возвращать ответ. Ответ на стороне клиента можно получить через хук useActionData, в котором, например, могут приходить ошибки валидации. Экспорт default – это сам компонент.
Remix состоит из компилятора, серверного HTTP обработчика, серверного фреймворка и браузерного фреймворка. С помощью компилятора каждый модуль превращается в браузерный бандл и в бандл для серверного HTTP обработчика. Серверные HTTP обработчики позволяют запускать сервер как на Node.js серверах, так и на serverless серверах, например, Cloudflare Workers. Серверный фреймворк позволяет для каждого роута добавить функцию подготовки данных loader и функцию обработки формы action. Браузерный фреймворк позволяет работать приложению как SPA, дает возможность использовать хуки для взаимодействия с сервером, а также предзагружает ресурсы для страниц сайта.
Пример Remix модуля:
// routes/todos.js
import { redirect, Form } from "remix";
export async function loader() {
return fakeGetTodos();
}
export async function action({ request }) {
const body = await request.formData();
const todo = await fakeCreateTodo({
title: body.get("title")
});
return redirect(`/todos/${todo.id}`);
}
export default function Todos() {
const data = useLoaderData();
return (
<div>
<TodoList todos={data} />
<Form method="post">
<input type="text" name="title" />
<button type="submit">Create Todo</button>
</Form>
</div>
);
}
https://remix.run/docs/en/v1
🇺🇸 blog.plasmic.app/posts/w…ttention
const { data: pokemonData, isLoading } = useWithToast(
useSWR<PokemonList>('https://pokeapi.co/api/v2/pokemon?limit=20')
);
theodorusclarence.com/blog/re…-pattern