Олег
28 мая 2021 г., 21:07

Не получается понять, как работает компонент.

Вообщем преамбула следующая, есть страница с каталогом карточек. Нужно на этой страницы по клику на карточку открыть эту карточку на другой странице.

Вот есть уже готовый код, но я не могу понять, как он работает. Примерно такую же задачу делал, но через Context API, но оказывается можно сделать по другому.

Сама страница, которая отвечает за вывод карточки:

import React from 'react'; import style from './Pokemon.module.scss'; import Layout from '../../components/Layout'; import Heading, { HeadingType } from '../../components/Heading'; export interface PokemonProps { id: string | number; } const mockPockemon: any = { abilities: ['blaze', 'solar-power'], exp: 240, height: 17, id: 6, img: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/6.png', is_default: true, name: 'charizard', order: 7, stats: { hp: 78, attack: 84, defense: 78, specialAttack: 109, specialDefense: 85, speed: 100 }, types: ['fire', 'flying'], weight: 905, }; const Pokemon: React.FC<PokemonProps> = (props) => { const { id } = props; const { img, name, exp, abilities, stats, types } = mockPockemon; const { hp, attack, defense, specialAttack, specialDefense } = stats; return ( <div className={style.root}> <Layout className={style.contentWrap}> <div className={style.pictureWrap}> <img src={img} alt={name} /> </div> <p> {types.map((it: string) => ( <span>{it}</span> ))} </p> <div className={style.stats}> <div className={style.header}> <Heading type={HeadingType.h3} className={style.name}> {name} </Heading> <p className={style.generation}>Generation 1</p> <div className={style.id}>{id}</div> </div> <div className={style.row}> <p>Abilities</p> {abilities.join(' - ')} </div> <div className={style.row}> <p> Healthy Points <br /> <span>{hp}</span> </p> <p> Experience <br /> <span>{exp}</span> </p> </div> <div className={style.row}> <div> <p>{defense}</p> <p>Defence</p> </div> <div> <p>{attack}</p> <p>Attack</p> </div> <div> <p>{specialAttack}</p> <p>Sp Attack</p> </div> <div> <p>{specialDefense}</p> <p>Sp Defense</p> </div> </div> </div> </Layout> </div> ); }; export default Pokemon;
Здесь меня во-первых меня заинтересовал странный вариант объявления пропсов:
const Pokemon: React.FC<PokemonProps> = (props) => { const { id } = props; const { img, name, exp, abilities, stats, types } = mockPockemon; const { hp, attack, defense, specialAttack, specialDefense } = stats;
Я правильно понимаю, что в props у нас попадает только id назначенный константой через деструктуризацию. А mockPockemon и stats мы создаем только для того, чтобы использовать в самом компоненте или все-таки они тоже каким-то образом появляются в пропсах?



Компонент самой карточки, которая отображается на странице каталога карточек:

import React from 'react'; import cn from 'classnames'; import Heading, { HeadingType } from '../Heading'; import style from './PokemonCard.module.scss'; import { IPokemon } from '../../interface/pokemons'; interface IPokemonCard { pokemon: IPokemon; onCardClick: (arg: any) => void; } const PokemonCard: React.FC<IPokemonCard> = (props) => { const { pokemon, onCardClick } = props; const { name, stats, types, img } = pokemon; return ( <div className={style.root} onClick={onCardClick} onKeyPress={onCardClick} role="button" tabIndex={0}> <div className={style.infoWrap}> <Heading type={HeadingType.h4} className={style.titleName}> {name} </Heading> <div className={style.statWrap}> <div className={style.statItem}> <div className={style.statValue}>{stats.attack}</div> Attack </div> <div className={style.statItem}> <div className={style.statValue}>{stats.defense}</div> Defense </div> </div> <div className={style.labelWrap}> {types.map((it: string) => ( <span className={cn(style.label, style[it as keyof typeof style])}>{it}</span> ))} </div> </div> <div className={cn(style.pictureWrap, style[types[0] as keyof typeof style])}> <img src={img} alt={name} /> </div> </div> ); }; export default PokemonCard;
Вот здесь я так понимаю все что назначено через деструктуризацию попадает в пропсы, так как pokemon присваивается в props. Помимо этого мы еще добовляем событие onCardClick повешеное на style.root.



Страница где выводится каталог карточек:
import React, { useState } from 'react'; import { navigate } from 'hookrouter'; import Layout from '../../components/Layout'; import Heading, { HeadingType } from '../../components/Heading'; import PokemonCard from '../../components/PokemonCard'; import useDebounce from '../../hook/useDebounce'; import useData from '../../hook/getData'; import { IPokemons } from '../../interface/pokemons'; import { parsePokemons } from '../../adapters/pokemons'; import { LinkEnum } from '../../routes'; import style from './Pokedex.module.scss'; interface IQuery { name?: string; } const PokedexPage = () => { const [searchValue, setSearchValue] = useState<string>(''); const debouncedValue = useDebounce(searchValue, 500); const [query, setQuery] = useState<IQuery>({}); const { data, isLoading, isError } = useData<IPokemons>('getPokemons', query, [debouncedValue]); const parsedPokemons = data && parsePokemons(data.pokemons); const handleSearchChange = (evt: React.ChangeEvent<HTMLInputElement>) => { setSearchValue(evt.target.value); setQuery((state) => ({ ...state, name: evt.target.value, })); }; const handleCardClick = (id: number) => { navigate(LinkEnum.POKEMON.replace(':id', String(id))); }; if (isLoading) { return <div>Loading...</div>; } if (isError) { return <div>Error!</div>; } return ( <div className={style.root}> <Layout className={style.contentWrap}> <Heading type={HeadingType.h3} className={style.heading}> {data && data.total} <strong>Pokemons</strong> for you to choose your favorite </Heading> <input type="text" className={style.search} placeholder="Encuentra tu pokémon..." value={searchValue} onChange={handleSearchChange} /> filters <div className={style.pokemonList}> {parsedPokemons && parsedPokemons.map((it) => ( <PokemonCard key={it.id} pokemon={it} onCardClick={() => handleCardClick(it.id)} /> ))} </div> pagination </Layout> </div> ); }; export default PokedexPage;

Все что связано с поиском по карточкам меня не интересует. Меня интересует, как работает именно передача карточки на страницу Pokemon.

Я вижу, что мы мапой проходимся по parsedPokemons в котором у нас лежат данные и выводим их компонентом PokemonCard.

{parsedPokemons && parsedPokemons.map((it) => ( <PokemonCard key={it.id} pokemon={it} onCardClick={() => handleCardClick(it.id)} /> ))}
Так же мы из пропсов PokemonCard вызываем пропс onCardClick и через стрелочную функцию вызываем handleCardClick, который у нас объявлен в этом компоненте, который нас перекидывает на страницу Pokemon. Хорошо он нас перекинул - в хендлере вижу только формирование страницы host/pokemon/id и переброс на страницу, а как сам вывод карточки покемона осущевстляется на самой странице? По моей логике туда тоже должна прийти тогда дата из апишки из которой мы по id допустим делаем вывод, но я не вижу, чтобы туда дата приходила, кроме приходящего id больше ничего не вижу, может я чего-то не догоняю.

И поделись информацией, какие еще есть варианты, чтобы осуществить даную логику клик по карточке и вывод ее на другой странице.
>> Я правильно понимаю, что в props у нас попадает только id назначенный константой через деструктуризацию. А mockPockemon и stats мы создаем только для того, чтобы использовать в самом компоненте или все-таки они тоже каким-то образом появляются в пропсах?

Олег, не все переменные, что ты видишь внутри компонента, приходят через пропсы. Это могут быть просто другие переменные и константы из более высокой области видимости. В твоем случае mockPockemon - это просто константа, объявленная выше.
const mockPockemon: any = { abilities: ['blaze', 'solar-power'], exp: 240, height: 17, id: 6, img: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/6.png', is_default: true, name: 'charizard', order: 7, stats: { hp: 78, attack: 84, defense: 78, specialAttack: 109, specialDefense: 85, speed: 100 }, types: ['fire', 'flying'], weight: 905, };
В IDE ты можешь курсор поставить на нужную переменную и нажать F12, чтобы IDE тебе показала откуда берется эта переменная.

В этом, скорее всего, и кроется ответ на все твои здесь вопросы. Условно, когда ты переходишь на страницу покемона (имею ввиду компонент Pokemon), то у тебя на вход через пропсы заходит только id, а данные покемона берутся из вот этой единой константы mockPockemon (для всех покемонов). В таком случае, судя по всему, у тебя все карточки отображаются одинаково. А чтобы такого не было, тебе надо в компоненте запилить еще получение данных покемона. То есть на вход у тебя есть id, надо затянуть из источника данные конкрертно этого покемона.

Понятно значит все-таки здесь нет затягивания покемона, я просто на гитхабе этот код смотрю, думал он рабочий. Весь мозг уже себе сломал не мог понять, как же он все-таки получает покемона. Значит все-таки я был частично прав, что должно быть еще получение даных для отоброжения покемона (про константу я понял). Правильно понимаю, что тогда нужно примерно реализовать такую же логику, как у нас в компоненте PokedexPage:

Сначала запускаем получение данных из самописного хука useData, который отвечает за получение данных.
const { data, isLoading, isError } = useData<IPokemons>('getPokemons', query, [debouncedValue]);
Потом скорей всего нужно объявить пустой useState и потом через useEffect туда положить покемона, только не совсем понимаю, как это все реализовать. Тут еще нужно в useEffect скорей всего сначала получить id из урла, а потом уже по этому id добовлять покемона в useState. Правильный вообще ход мыслей или вообще не туда?
В целом ход мыслей правильный. Но как именно тебе это реализовать, придется думать самому, потому что у тебя там фаербейз и прочие частные тонкости. И именно поэтому я и говорил изучать мой движок, потому что там все это из коробки идет. Ну, может со временем ты это осознаешь.
Нет в данном случае все без firebase здесь данные просто с апишки по урлу приходят http://zar.hosthot.ru/api/v1/pokemons есть конфиг и пару функций которые его обработывают и потом все это хуком получаем, сложный такой вариант получения данных получился (возможно, может и несложный, мне пока сложно оценить), я так понял есть специальные либы для этих целей, которые упрощают этот процес, здесь просто все самопис. Ладно буду дальше кумекать, как получить данные.

Да твоего движка пока не до рос, обязательно потом next буду изучать. Просто сейчас нужно пока хоть с базовыми вещами разобраться. У тебя я так понимаю весь этот процес еще сложней для меня будет, там у тебя graphql и appolo с ними вообще еще не разбирался.

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