Как и в предыдущем уроке, здесь будет показано эталонное изображение структуры и содержимого моего файла, чтобы вы могли сравнить.
Создание клиента Sanity
Нам нужно создать способ, с помощью которого наше приложение сможет получать данные, которые мы храним в Sanity. К счастью, Sanity делает это очень просто. Подробнее о том, что я использую, вы можете прочитать здесь. Клиент Sanity
npm i -g @sanity/client
Это позволит установить клиент Sanity глобально для использования в любом проекте.
Нам нужно создать в корне папку lib
и файл в этой папке sanity.js
и вставить этот код в новый файл.
const sanityClient = require("@sanity/client");
const client = sanityClient({
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET,
apiVersion: "2021-03-25", // use current UTC date - see "specifying API version"!
useCdn: true, // `false` if you want to ensure fresh data
});
export default client;
Этот код можно найти в документации пакета Sanity Client.
Получение постов
В вашем файле /pages/index.js
(не в вашем /studio/schemas/index.js
), в самом низу, вам нужно создать функцию async/await для получения данных.
export async function getStaticProps() {}
В верхней части страницы необходимо импортировать клиент, который мы только что создали.
import client from "../lib/sanity";
Вы можете, и должны, прочитать о getStaticProps
здесь NextJS docs.
На этой странице объясняется, что делает эта функция, как она работает и когда ее использовать. Одна из причин использовать ее, если данные поступают из безголовой CMS
, а именно так мы и планируем ее использовать.
Внутри нашей новой функции мы сделаем запрос GROQ, чтобы указать ей, какие данные искать, как их упорядочить и какие части этих данных мы хотим получить обратно.
export async function getStaticProps() {
const query = `*[_type == "post"] | order(publishedAt desc) {
_id,
title,
publishedAt,
'slug': slug.current,
body
}`;
const posts = await client.fetch(query);
return {
props: { posts },
};
}
Я собираюсь разбить каждую часть этой функции на части, чтобы было легче понять, потому что простое копирование и вставка кода без знания того, что означает каждая часть, не будет очень полезным в дальнейшем.
-
const query = `...`
- Очевидно, что мы просто объявляем переменную для нашего запроса, чтобы использовать ее позже. Но здесь важен тот факт, что наш код в запросе окружен обратными тиками.
-
*[_type == "post"]
- Это говорит нашему клиенту, что нужно взять каждую запись в нашей базе данных, которая имеет
name
post - Вы можете видеть, что мы назвали наши записи, когда создавали схему ‘Post’. Именно на это ссылается запрос
"post"
.
- Это говорит нашему клиенту, что нужно взять каждую запись в нашей базе данных, которая имеет
-
| order(publishedAt desc)
- Все достаточно просто, это говорит нашему запросу вернуть «посты», которые он нашел, в порядке убывания по дате публикации. Это означает, что самые новые посты возвращаются первыми.
{
_id,
title,
publishedAt,
'slug': slug.current,
body
}
- Мы сообщаем запросу на выборку, какие данные мы хотим получить. В этом и заключается сила GROQ — вы получаете только те данные, которые вам нужны. Без этой части вы бы получили все данные ‘post’, которые выглядят примерно так.
{
"_createdAt": "2022-07-17T00:48:06Z",
"_id": "f026b8eb-0fc6-4a58-8494-789def2703ff",
"_rev": "IvZ71YmXkO22WtmwIxDKV0",
"_type": "post",
"_updatedAt": "2022-07-17T00:48:06Z",
"body": (5) [{…}, {…}, {…}, {…}, {…}],
"publishedAt": "2022-07-17T00:45:31.070Z",
"slug": {
"_type": "slug",
"current": "the-art-of-creating-a-post"
},
"title": "The art of creating a post"
}
Это гораздо больше информации, чем нам нужно. Поэтому мы скажем нашему запросу возвращать нам только _id, title, publishedAt, slug и body.
Для любопытных, slug записывается как 'slug': slug.current
, потому что, как вы можете видеть выше, slug возвращает объект, но нам нужно только значение current
, а не _type
. Поэтому мы возвращаем slug.current
, но присваиваем его имени ключа slug
.
-
const posts = await client.fetch(query);
- Простой запрос на выборку, который использует
client
, который мы создали и импортировали в этом уроке, переменнуюquery
, которую мы только что добавили, и присваивает возврат новой переменнойposts
.
- Простой запрос на выборку, который использует
-
return { props: { posts } };
- Возвращаем реквизит с нашей переменной posts, чтобы наш компонент мог ее использовать.
Рендеринг наших постов
Вернемся в начало нашего файла /pages/index.js
, в котором мы сейчас находимся, и добавим нашу переменную posts
, которую мы только что получили, в наш компонент Home
.
export default function Home({ posts }) {
return <h1>Hi :D</h1>;
}
Мы можем извлечь данные posts
из значения props
с помощью метода destructuring
.
Теперь создадим элементы HTML из нашего массива posts
. Мы сделаем это путем сопоставления данных и сохранения их в переменной.
const postsElements = posts.map((post, index) => (
<div key={index}>
<p>{new Date(post.publishedAt).toDateString().slice(4)}</p>
<h3>{post.title}</h3>
</div>
));
Это вернет div
с тегом p
и h3
для каждого поста.
Нам нужен внешний div
просто потому, что вам нужно, чтобы несколько элементов были обернуты в 1, чтобы вы могли вернуть его. Возвращение p
и h3
без обертывания их во что-то приведет к ошибке. Мы также добавляем к этому key
с index
его позиции в массиве, чтобы React не кричал на нас. (об этом очень полезно почитать, это помогло мне много часов отлаживать, когда я начинал изучать React).
Мы добавляем тег p
со значением publishedAt
, превращенным в дату, в строку, чтобы HTML мог ее прочитать, и slice
отделяем первые 4 символа строки (по сути, удаляя день недели).
И, наконец, h3
с title
нашего поста.
Теперь, если вы добавите эту переменную в ваш return (обернув ее снова во внешний div по той же причине, что и выше, вы должны вернуть 1 внешний HTML-элемент), вот так.
return <div>{postsElements}</div>
Закрыв текущий npm run dev
и запустив его снова, вы увидите все ваши посты (но только заголовок и дату публикации) на главной странице по дате их публикации.
Сейчас это довольно уродливо, но не в этом суть. Я создам будущий урок или, возможно, отредактирую этот, чтобы добавить методы, которые я собираюсь использовать для стилизации. Пока же я просто хочу выложить это, чтобы помочь тем, кому это нужно.
Будущие уроки
Вот и все для этой части. Возможно, я переборщил с объяснением каждой мелочи, но все эти понятия очень фундаментальны и будут использоваться в дальнейшем в этом проекте (и, скорее всего, в ваших будущих проектах). Поэтому я хотел помочь вам понять, как все работает, чтобы у вас было достаточно знаний, чтобы изучить и сделать что-то самостоятельно.
В следующей части я объясню, как создавать динамические урлы для ваших постов, чтобы вы могли видеть полную информацию о них на странице.
Ссылки
— Структура файла
— /pages/index.js
import client from "../lib/sanity";
export default function Home({ posts }) {
const postsElements = posts.map((post, index) => (
<div key={index}>
<p>{new Date(post.publishedAt).toDateString().slice(4)}</p>
<h3>{post.title}</h3>
</div>
));
return <div>{postsElements}</div>;
}
export async function getStaticProps() {
const query = `*[_type == "post"] | order(publishedAt desc) {
_id,
title,
publishedAt,
'slug': slug.current,
body
}`;
const posts = await client.fetch(query);
return {
props: { posts },
};
}
— /lib/sanity.js
const sanityClient = require("@sanity/client");
const client = sanityClient({
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET,
apiVersion: "2021-03-25", // use current UTC date - see "specifying API version"!
useCdn: true, // `false` if you want to ensure fresh data
});
export default client;