Попов Дмитрий
23 янв. 2021 г., 14:32

Проект #pivo-moscow. Обсуждение Задачи №1. На конечной странице пива вывести название пива и картинку

Начальная картина.
Со страницы /beers/ ссылкой передается id пива. Хук useRouter() принимает запрос, что позволяет id вернуть через {router.query.id}.

Первым делом давайте посмотрим сюда https://github.com/linklib/pivo-moscow/blob/master/src/gql/Beer.graphql на предмет данных, которые можно получить с пивкарты: для пива это id, name, uri. Нам не хватает картинки.
На https://pivkarta.ru/api/ можно посмотреть все ,что можно вытащить с пивкарты. Сейчас нам не хватает картинки: это будет image, то есть запрос станет таким:

query beers($where: BeerWhereInput, $first: Int = 3, $skip: Int) { beersConnection(where: $where, first: $first, skip: $skip) { aggregate { count } edges { node { ...beer } } } } fragment beer on Beer { id name uri image places { id price Place { ...place } } } fragment place on Place { id name uri }
После внесения изменений следует перегененировать типы:
yarn generate:types

За основу предлагаю взять эту страницу https://github.com/linklib/pivo-moscow/blob/master/src/pages/Beers/index.tsx - здесь есть запрос.
То есть нам надо на основе запроса, который возвращает данные по пиву (по дефолту первых трех в бд), составить зарпрос, который вернет данные только нужного пива по id.

У меня задачу выполнить получилось, но явно через точкуЖ, нужен совет, как вывести поля без перебора полученного массива - ведь мы знаем, что придет только один объект, по запрашиваемому id. Получившийся код [id].tsx такой:


import { useRouter } from 'next/dist/client/router' import { Page } from 'src/pages/_App/interfaces' import { NextSeo } from 'next-seo' import { ParsedUrlQuery } from 'querystring' import React from 'react' import { BeerFragment, //BeersDocument, //BeersQuery, BeersQueryVariables, useBeersQuery, } from 'src/modules/gql/generated' const getVariables = (query: ParsedUrlQuery): BeersQueryVariables => { const first = query.first && typeof query.first === 'string' ? parseInt(query.first) : undefined const idbeer:any = query.id return { first, where: { id: idbeer, }, } } const BeerPage: Page = () => { const router = useRouter() const variables = getVariables(router.query) const response = useBeersQuery({ variables, }) const beers: BeerFragment[] = [] response.data?.beersConnection.edges.map((n) => { if (n?.node) { beers.push(n.node) } }) //console.log('beers', beers[0]); //console.log('variables', variables); //if(beers[0]) { var beername = beers[0].name var beerimage = beers[0].image //} return ( <> <NextSeo title={beername} /> {/*router.query.id*/} <h1>{beername}</h1> <img src={"https://pivkarta.ru/images/resized/thumb/" + beerimage} alt={beername}/> </> ) } export default BeerPage
Предлагаю пробежсть по коду на предмет что есть что.

-----

const getVariables = (query: ParsedUrlQuery): BeersQueryVariables => { const first = query.first && typeof query.first === 'string' ? parseInt(query.first) : undefined const idbeer:any = query.id return { first, where: { id: idbeer, }, } }

-----
Здесь мы получаем id из урла и пихаем в idbeer. Объявление переменной связано с желанием избежать ругани ts на не сообветствие типов данных. Изначально было where: { id: query.id, }.

Дальше потребовалось перебрать массив и быбрать первый объект, что явно какая-то дичь. А как не дичь - ищу)

Нужна помощь!

Понял, что не там пишу код страницы и перенес в src/pages/Beer/index.tsx
А в [id].tsx оставил export { default } from 'src/pages/Beer'


Дима, зайди в экшены https://github.com/linklib/pivo-moscow/actions и активируй их, чтобы тесты на гитхабе выполнялись.
И лучше не все в мастер сразу делай, а выноси в логические бранчи и пуши на гитхаб, чтобы можно было видеть что ты делаешь, ревьюить, скачивать себе править и т.п.
А за основу имело смысл взять вот эту страницу: https://github.com/freecode-academy/freecode.academy/blob/master/pages/technologies/%5Bid%5D.tsx

Ведь у нее такой же роутинг как и у тебя - по id. Можно его страницу буквально брать 1:1 и просто заменить Technology на Beer

Запрос тоже по образу и подобию можешь воткнуть https://github.com/freecode-academy/freecode.academy/blob/master/src/pages/Technologies/Technology/gql/technology.graphql.

Затем выполни yarn generate:types, и если ОК выполнится, у тебя будет метод для запросов useBeerQuery. Все, дальше дело техники: получи данные и выведи в нужном тебе виде.

Пробуй. Если что не так, выливай на гитхаб и маякуй.
Нужна помощь.
Бранч https://github.com/linklib/pivo-moscow/tree/beer_page

Странная ситуация - страница пива то отдается, то нет. Причина - не всегда прилетает объект из базы.
Дима, давай ссылки на конкретные участки кода и файлы, как это делаю я. Сейчас я как телепат: личная задача - выяснить, где что вообще у тебя не так. Задача стоит по конечной странице пива, а в бранче это страница пива - заготовка без каких-либо запросов.
И у тебя там коммит последний 9 дней назад
Пардонте, разбираюсь
Даже с учетом того, что у тебя там были ошибки и ты выливал без выполнения проверок, имело смысл выполнить yarn format, чтобы код весь в порядок привести :) Читалось бы лучше.
Где смайл "покраснеть"?
А меж тем там есть вот такой комментарий:
/** * Важно, чтобы все переменные запроса серверные и фронтовые совпадали, * иначе при рендеринге не будут получены данные из кеша и рендер будет пустой. */
Как я и рассказывал, реакт не умеет в асинхронность. Он не умеет дожидаться результатов Ajax-запросов и выполняется сразу, даже если данных нет. Собственно, это и происходит в твоем случае.
А для того, чтобы данные можно было дождаться на стороне сервера, в next-js как раз и имеются методы типа getInitialProps, чтобы можно было прописать подгрузку данных с ожиданием, и только после этого перейти непосредственно к отрисовке реакта.

Смотри, как это сделано, к примеру, на странице задачи: https://github.com/freecode-academy/freecode.academy/blob/7551c12be55642d471d1f7ff747c77d09ff3b3d2/src/pages/Tasks/Task/index.tsx#L58-L74

Если не справишься, маякни, отправлю ПР.
>> Где смайл "покраснеть"?

Попозже обязательно добавлю :)
Всё-таки где-то накосячил - отдает 404
https://github.com/Pivkarta/nextjs/commit/f4c8d1280d46b519ddc74219c03e17bc5467f550

Можешь кротко описать, кто что в коде делает, если не сложно? Для понимания процессов и осмысленности.
Для начала признайся, использовал git commit --amend?

Что здесь написано? statusCode: !result.data.object ? 404 : undefined
А у тебя есть объект result.data.object?

Уверен, что TypeScript тебе усиленно говорил "Нет свойства object в объекте result.data", но ты на это забил.
>>Для начала признайся, использовал git commit --amend? - нет, а надо было?
Хотя нет, object у тебя вроде как есть в запросе https://github.com/Pivkarta/nextjs/blob/f4c8d1280d46b519ddc74219c03e17bc5467f550/src/gql/Beerinfo.graphql

А console.log(result.data) на стороне сервера в терминал что выводит?

Сейчас скачаю, проверю.
>> нет, а надо было?
Нет, не надо было. Просто сообщение на гитхабе "This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.".
Но ты видимо просто не на тот репозиторий ссылку дал. Но в целом не очень важно.

P.S. Ага, коммит у тебя в этой репе: https://github.com/linklib/pivo-moscow/tree/beer_page
Точно так ,здесь должен быть. А он на Pivkarta/nextjs кидает. Странно
У гитхаба часто такое. Особенно у него полная дичь в виджете сравнения. Просто бесит его поведение. И бесит именно тем, что понятно почему у него так все работает и то, почему они это не исправят?
А при заходе на страницу пива тебя вот такая штука не напрягала:

Server Error

Error: Response not successful: Received status code 400
This error happened while generating the page. Any console logs will be displayed in the terminal window.
А в терминале детали:
[HPM] Client disconnected
beers 0127b5076bc8040f82c0f7e74
[GraphQL error]: Message: Unknown type "BeerWhereUniqueInput". Did you mean "UserWhereUniqueInput", "TimerWhereUniqueInput", "CareerWhereUniqueInput", "TeamWhereUniqueInput", or "TestWhereUniqueInput"?, Location: [object Object], Path: undefined
[GraphQL error]: Message: Cannot query field "beer" on type "Query". Did you mean "user" or "career"?, Location: [object Object], Path: undefined
[GraphQL error]: Message: Unknown type "Beer". Did you mean "User", "Career", or "Letter"?, Location: [object Object], Path: undefined

У тебя просто косяк в GraphQL-запросе.
Прежде чем вставлять запрос, проверяй его в плейграунде.
Я бы такое заметил) Не было ошибки
Серьезно? Не было? А зайди на страницу пива и обнови страницу.
>>Прежде чем вставлять запрос, проверяй его в плейграунде. Я его оттуда и взял
>>Серьезно? Не было? А зайди на страницу пива и обнови страницу. --
Отдает страницу нормально, даже без 404
Сейчас перепроверю, может у него путаница с АПИ эндпоинтами.
>> Сейчас перепроверю, может у него путаница с АПИ эндпоинтами.
Это я запросто мог устроить)))
А вот на такие вещи ты обращаешь внимание?
const name: any = response.data?.object.name (property) object?: ({ __typename?: "Beer" | undefined; } & Beer_Fragment) | null | undefined Object is possibly 'null' or 'undefined'.ts(2533)
И, во-первых, здесь не надо указывать :any, тайпскрипт сам поймет.
Во-вторых, максимально старайся избегать any. any рушит сам смысл типизации.

Да, это вижу: как в моем случае правильно задать const name ?

Все, я разобрался.

А есть еще вот точка входа (конфиг аполло-клиента): https://github.com/linklib/pivo-moscow/blob/beer_page/src/lib/apolloClient.ts#L37

Вот не надо было лезть в конфиг, а надо было создать .env и указать нужный API_ENDPOINT. Для этого есть сэмпл: https://github.com/linklib/pivo-moscow/blob/beer_page/.env.sample

Получается, что когда на стороне сервера выполнялся АПИ-запрос, он летел на дефолтный АПИ, то есть на https://api.prisma-cms.com, а там, конечно же, ничего про пиво нет :)

Так что создай файл .env с содержимым
API_ENDPOINT=https://pivkarta.ru/api/

И будет тебе счастье.
<< Да, это вижу: как в моем случае правильно задать const name ?

const name = response.data?.object?.name

У тебя же object возможен пустым, так что ему и добавляешь вопрос.
Спасибо! Вот знал ,что если косячить - так по полной))
Но что интересно: .env у меня есть. Но таки да, в конфиг ручками залазил...
То есть по идее в аполло должен был из .env эндпоинд подтащить?
А в .env что прописано?

На всякий случай уточню: я локально у себя создал .env и указал конкретный эндпоинт, и у меня все заработало, и страница списка пива и конечная страница пива, в том числе с перезагрузкой и без JS.
API_ENDPOINT=https://pivkarta.ru/api/
Дай адрес конкретной страницы, которая отдает 404 (локальный)
В том и дело, что больше не отдаёт 404, все работает
Но в какой момент стало работать - не очень понятно.

Наблюдаю.
Ты учитывай, что это дев-среда. Она не очень стабильная. К примеру у меня вот так сейчас бэк отвалился:

apiProxy onError err Error: getaddrinfo ENOTFOUND pivkarta.ru at GetAddrInfoReqWrap.onlookup [as oncomplete] (node:dns:69:26) { errno: -3008, code: 'ENOTFOUND', syscall: 'getaddrinfo', hostname: 'pivkarta.ru' } /disk480/www/pivkarta.ru/pivo-moscow/server/index.ts:23 res.writeHead(500, { ^ TypeError: res.writeHead is not a function at ProxyServer.onError (/disk480/www/pivkarta.ru/pivo-moscow/server/index.ts:23:9) at ProxyServer.emit (/disk480/www/pivkarta.ru/pivo-moscow/node_modules/http-proxy/node_modules/eventemitter3/index.js:204:33) at ClientRequest.onOutgoingError (/disk480/www/pivkarta.ru/pivo-moscow/node_modules/http-proxy/lib/http-proxy/passes/ws-incoming.js:157:16) at ClientRequest.emit (node:events:379:20) at ClientRequest.EventEmitter.emit (node:domain:470:12) at TLSSocket.socketErrorListener (node:_http_client:494:9) at TLSSocket.emit (node:events:379:20) at TLSSocket.EventEmitter.emit (node:domain:470:12) at emitErrorNT (node:internal/streams/destroy:188:8) at emitErrorCloseNT (node:internal/streams/destroy:153:3) at processTicksAndRejections (node:internal/process/task_queues:81:21) [nodemon] app crashed - waiting for file changes before starting...
У моего провайдера очень плохие DNS-сервера и постоянно тупит. Вот локально это запускается через nodemon и он в таких случаях не перезапускается, он ожидает действий от пользователя.
Вот в таких случаях, если ты зашел на страницу пива, потом ушел на другую страницу и опять зашел на страницу пива, только уже другого пива, некст не будет подтягивать никаких скриптов и т.п., он просто запросит данные пива через GraphQL-запрос. А сервер отвалился... Он не может получить данные и говорит "404, данных-то нет".

Это один из вариантов.
На проде это все более стабильно.

Понял, спасибо!

По этой задаче остальсь вьюху сделать и как-то закинуть в нужную ветку изменения. И переходить к второй задаче)
Перечитай комменты в этом топике. Там наверняка есть то, что тебе нужно.
Всё-таки вьюху в отдельную задачу выведу
Коммиты по первой задаче здесь - https://github.com/Pivkarta/nextjs/compare/master...linklib:beer_page?expand=1
Рзберусь, как закинуть изменения - закину)

Добавить комментарий