Зачем передавать данные из getServerSideProps в компоненты?
Итак, давайте начнём с простого вопроса: зачем вообще передавать данные из getServerSideProps? Ответ: чтобы ваш компонент знал, какую информацию отображать.
getServerSideProps вызывается на сервере (или в среде выполнения SSR), обрабатывает запрос, получает данные и отправляет их на страницу как пропсы. Эти данные могут быть как простыми объектами, так и сложными структурами.
Представьте, что вы разрабатываете страницу профиля пользователя. Для этого вам нужно изначально получить данные пользователя с сервера (например, по API). Вот где пригодится getServerSideProps.
Разбор простого примера передачи данных
Начнём с базового примера. Предположим, мы разрабатываем страницу блога, которая отображает заголовок и описание поста. Данные поста мы будем получать с сервера через getServerSideProps.
Что мы будем делать:
- Создадим страницу
pages/post/[id].tsx. - Используем
getServerSidePropsдля получения поста по ID. - Передадим данные поста как пропсы в компонент.
Вот код:
// pages/post/[id].tsx
import { GetServerSideProps } from 'next';
type PostProps = {
post: {
id: string;
title: string;
content: string;
};
};
const PostPage = ({ post }: PostProps) => {
return (
<div>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
);
};
// Реализация getServerSideProps
export const getServerSideProps: GetServerSideProps = async (context) => {
const { id } = context.params as { id: string };
// Симулируем запрос к API
const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
const post = await response.json();
return {
props: { post }, // Передаём данные в компонент
};
};
export default PostPage;
Разберём код:
getServerSideProps: получает параметрidиз маршрутаcontext.paramsи выполняет запрос к API для получения данных поста.props: возвращаем объект с ключомprops, который содержит данные нашего поста. Next.js автоматически передаёт их в компонент страницы.Типизация: интерфейс
PostPropsописывает структуру пропсов, ожидаемых компонентомPostPage.
Когда пользователь откроет страницу /post/1, Next.js выполнит getServerSideProps, получит данные поста с ID 1 и передаст их в PostPage для рендеринга. Всё просто, как console.log("Hello, world!").
Важные моменты про передачу данных
Передача сложных объектов: если данные, которые вы передаёте, содержат функции,
Dateили необычные структуры (например,Map,Set), они не будут корректно сериализованы. Помните: только JSON-совместимые структуры могут быть переданы черезprops.Пример данных, которые могут вызвать проблемы:
const data = { date: new Date(), // Date объект map: new Map(), // Map структура };Решение: преобразуйте сложные данные в строки или массивы:
const serializedData = { date: data.date.toISOString(), // Преобразуем дату в строку map: Array.from(data.map.entries()), // Преобразуем Map в массив };Размер передаваемых данных: передаваемые через
propsданные добавляются в HTML-ответ сервера, что увеличивает размер страницы. Если данных действительно много, лучше загружать их на клиенте (клиентская пагинация, lazy loading и т.п.).
Управление данными в компоненте
Часто передача данных из getServerSideProps — это лишь первый шаг. Давайте разберем, как их можно использовать внутри нашего компонента.
Рендеринг данных: как мы видели в примере выше, данные просто передаются через пропсы и рендерятся. Это стандартный подход.
Работа с состоянием в компоненте: если данные нужно изменять на стороне клиента, можно использовать хук
useStateдля управления ими. Например:const PostPage = ({ post }: PostProps) => { const [content, setContent] = React.useState(post.content); const updateContent = () => { setContent(content + " Еще немного текста..."); }; return ( <div> <h1>{post.title}</h1> <p>{content}</p> <button onClick={updateContent}>Добавить текст</button> </div> ); };Асинхронные операции на клиенте: если на стороне клиента нужно подгружать дополнительные данные, вы можете использовать
useEffectи API-запросы:const PostPage = ({ post }: PostProps) => { const [comments, setComments] = React.useState([]); React.useEffect(() => { const fetchComments = async () => { const res = await fetch(`/api/comments/${post.id}`); const data = await res.json(); setComments(data); }; fetchComments(); }, [post.id]); return ( <div> <h1>{post.title}</h1> <p>{post.content}</p> <h2>Комментарии:</h2> <ul> {comments.map((comment) => ( <li key={comment.id}>{comment.text}</li> ))} </ul> </div> ); };
Типизация данных в компоненте
Мы уже упомянули типизацию пропсов, но давайте рассмотрим несколько полезных советов:
Создавайте интерфейсы для данных:
определение интерфейсов помогает поддерживать порядок в коде. Важно описывать не только сами пропсы, но и данные, которые вы получаете с сервера:interface Post { id: string; title: string; content: string; } interface PostProps { post: Post; }Проверяйте типы на уровне API: иногда API может вернуть данные неожиданной структуры (например, пропущенные поля). Используйте библиотеки вроде
zodилиio-tsдля проверки данных перед их передачей в компонент.Пример использования с
zod:import { z } from 'zod'; const PostSchema = z.object({ id: z.string(), title: z.string(), content: z.string(), }); const post = await response.json(); const parsedPost = PostSchema.parse(post);
Полезные паттерны
Передача данных из getServerSideProps в компоненты становится еще мощнее, если использовать следующие подходы:
Контейнеры для логики: разделяйте логику получения и обработки данных от их отображения. Например, вы можете создать специальный контейнер-компонент, который передаёт данные в Dumb-компонент:
const PostContainer = ({ post }: PostProps) => {
return <PostView post={post} />;
};
const PostView = ({ post }: PostProps) => {
return (
<div>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
);
};
Переиспользование компонентов: если ваш компонент должен обрабатывать данные из разных источников, разделяйте их на универсальные части.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ