Comments

Начальная картина. Со страницы /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,\n  //BeersQuery,\n  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/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/index.tsx

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

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

Пробуй. Если что не так, выливай на гитхаб и маякуй.

Дима, давай ссылки на конкретные участки кода и файлы, как это делаю я. Сейчас я как телепат: личная задача - выяснить, где что вообще у тебя не так. Задача стоит по конечной странице пива, а в бранче это страница пива - заготовка без каких-либо запросов.

Даже с учетом того, что у тебя там были ошибки и ты выливал без выполнения проверок, имело смысл выполнить yarn format, чтобы код весь в порядок привести :) Читалось бы лучше.

Вот ты закомментировал вот этот блок: https://github.com/linklib/pivo-moscow/blob/eea51e0c4e4f90b708845b5cffb54c132b7cda4e/src/pages/Beer/index.tsx#L71-L156

А меж тем там есть вот такой комментарий:

    /**
     * Важно, чтобы все переменные запроса серверные и фронтовые совпадали,
     * иначе при рендеринге не будут получены данные из кеша и рендер будет пустой.
     */

Как я и рассказывал, реакт не умеет в асинхронность. Он не умеет дожидаться результатов Ajax-запросов и выполняется сразу, даже если данных нет. Собственно, это и происходит в твоем случае.

А для того, чтобы данные можно было дождаться на стороне сервера, в next-js как раз и имеются методы типа getInitialProps, чтобы можно было прописать подгрузку данных с ожиданием, и только после этого перейти непосредственно к отрисовке реакта.

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

Если не справишься, маякни, отправлю ПР.

нет, а надо было?

Нет, не надо было. Просто сообщение на гитхабе "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

У гитхаба часто такое. Особенно у него полная дичь в виджете сравнения. Просто бесит его поведение. И бесит именно тем, что понятно почему у него так все работает и то, почему они это не исправят?

А при заходе на страницу пива тебя вот такая штука не напрягала:

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 рушит сам смысл типизации.

Все, я разобрался. Ты залез в конфиг ручками https://github.com/linklib/pivo-moscow/blob/beer_page/src/config/index.ts#L6 А есть еще вот точка входа (конфиг аполло-клиента): 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.

В том и дело, что больше не отдаёт 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, данных-то нет".

Это один из вариантов.

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

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

По этой задаче осталось вьюху сделать и как-то закинуть в нужную ветку изменения. И переходить ко второй задаче)