#javascript #reactjs
#javascript #reactjs
Вопрос:
Я работаю с компонентом, который отображает список блогов. Затем для каждого блога я сопоставляю каждую категорию в массиве и отображаю ее. Код до сих пор:
import React, { useState, useEffect } from "react";
import { connect } from "frontity";
import { GridWrap, GridRow, GridColumn } from "emotion-flex-grid";
import Link from "@frontity/components/link";
import CardContainer from "./index.style";
const Card = ({ state, post, libraries, categoryName }) => {
console.log("post", post);
const [categoryNameFromId, setCategoryNameFromId] = useState();
useEffect(() => {
fetch(`${state.source.api}wp/v2/categories/${cat}`)
.then((response) => response.json())
.then((data) => {
setCategoryNameFromId(data);
});
}, []);
return (
<GridColumn width={[12, 12, 6, 4, 4]} p={["none", "none", "s", "m"]} py={["s", "s", "s", "m"]} style={{display: `flex`, alignSelf: `stretch`}}>
<CardContainer>
<div className="info">
{post.acf.featured_media ? <img src={post.acf.featured_media.url} /> : <img src="https://images.unsplash.com/photo-1510337550647-e84f83e341ca?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw=amp;ixlib=rb-1.2.1amp;auto=formatamp;fit=cropamp;w=3289amp;q=80" />}
<Link link={`blog/${post.slug}`} className="title">
{post.title.rendered}
</Link>
<GridRow className="date-time">
<p className="date">{post.formatted_date}</p>
<p className="reading-time">reading time</p>
</GridRow>
<div className="excerpt"><libraries.html2react.Component html={post.excerpt.rendered} /></div>
</div>
<div className="share">
<GridRow justify="between">
<p>
{post.categories.map((cat) => {
return (
<>
<span>{categoryNameFromId amp;amp; categoryNameFromId.name}, </span>
</>
)
})}
</p>
<div className="social-icons">
<i className="icon kap-facebook" />
<i className="icon kap-twitter" />
<i className="icon kap-linkedin" />
<i className="icon kap-mail" />
</div>
</GridRow>
</div>
</CardContainer>
</GridColumn>
)
}
export default connect(Card);
Проблема, с которой я сталкиваюсь, заключается в том, что я получаю сообщение об ошибке в консоли, в котором говорится, что «Неперехваченная ошибка ссылки: cat не определена». Поэтому я попробовал переместить крючок в другое место, вот так:
import React, { useState, useEffect } from "react";
import { connect } from "frontity";
import { GridWrap, GridRow, GridColumn } from "emotion-flex-grid";
import Link from "@frontity/components/link";
import CardContainer from "./index.style";
const Card = ({ state, post, libraries, categoryName }) => {
console.log("post", post);
return (
<GridColumn width={[12, 12, 6, 4, 4]} p={["none", "none", "s", "m"]} py={["s", "s", "s", "m"]} style={{display: `flex`, alignSelf: `stretch`}}>
<CardContainer>
<div className="info">
{post.acf.featured_media ? <img src={post.acf.featured_media.url} /> : <img src="https://images.unsplash.com/photo-1510337550647-e84f83e341ca?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw=amp;ixlib=rb-1.2.1amp;auto=formatamp;fit=cropamp;w=3289amp;q=80" />}
<Link link={`blog/${post.slug}`} className="title">
{post.title.rendered}
</Link>
<GridRow className="date-time">
<p className="date">{post.formatted_date}</p>
<p className="reading-time">reading time</p>
</GridRow>
<div className="excerpt"><libraries.html2react.Component html={post.excerpt.rendered} /></div>
</div>
<div className="share">
<GridRow justify="between">
<p>
{post.categories.map((cat) => {
const [categoryNameFromId, setCategoryNameFromId] = useState();
useEffect(() => {
fetch(`${state.source.api}wp/v2/categories/${cat}`)
.then((response) => response.json())
.then((data) => {
setCategoryNameFromId(data);
});
}, []);
return (
<>
<span>{categoryNameFromId amp;amp; categoryNameFromId.name}, </span>
</>
)
})}
</p>
<div className="social-icons">
<i className="icon kap-facebook" />
<i className="icon kap-twitter" />
<i className="icon kap-linkedin" />
<i className="icon kap-mail" />
</div>
</GridRow>
</div>
</CardContainer>
</GridColumn>
)
}
export default connect(Card);
И ошибка действительно исчезла. Но я почти уверен, что это неправильный способ сделать это. Разве нет способа сделать так, чтобы крючок находился в верхней части компонента? Любой вклад был бы замечательным!
Комментарии:
1. Не используйте useEffect hook внутри шаблона jsx.
2. @lissettdm я это знаю. вот почему я задал этот вопрос. Было бы более полезно, если бы у вас было потенциальное решение этого вопроса.
3. Помните, что a
useEffect
происходит после вашего первого рендеринга, так как таковойcategoryNameFromId
будет неопределенным. Что вы можете сделать здесь дляasync
подобных вещей, так это просто вернуть значение null, или, если ваше асинхронное действие может занять некоторое время, верните загрузку или что-то в этом роде .. например, до того, как ваш JSX сделает ..if (!categoryNameFromId) return null
, или загрузка и т.д..
Ответ №1:
Вызовы API используются для useEffect
выполнения, когда компонент смонтирован, в этом случае вы можете переместить за useEffect
пределы своего JSX и вызвать оттуда API.
useEffect(() => {
const promises = post.categories.map(cat => {
fetch(`${state.source.api}wp/v2/categories/${cat}`)
.then((response) => response.json());
});
Promise.all(promises).then(data => data.map(item => setCategoryNameFromId(item);))
}, []);
Комментарии:
1. хорошо, спасибо. и все же, как бы я тогда это отобразил?
Ответ №2:
Вам необходимо сохранить результат всех вызовов api. Для каждого элемента post.categories извлеките данные и сохраните их, после того как вы это сделаете, обновите состояние категорий:
const [categories, setCategories] = useState([]);
useEffect(() => {
const fetchData = async (cat) => {
const categoryIdData = await fetch(`${state.source.api}wp/v2/categories/${cat}`).then(res => res.json())
return categoryIdData;
}
const updateData = async () => {
let categories = [];
post.categories.map(async (cat) => {
const catData = await fetchData(cat);
categories.push(catData);
});
setCategories(categories);
}
updateData();
}, []);
затем в шаблоне:
<p>
{categories.map((cat) => (<>
<span>{cat.name}, </span>
</>)
}
</p>
Конечно, вы можете упростить это, используя Promise.all:
const [categories, setCategories] = useState([]);
useEffect(() => {
const updateData = async () => {
const categoriesFetch = post.categories.map(cat => fetch(`${state.source.api}wp/v2/categories/${cat}`).then(res => res.json()));
const categories = await Promise.all(categoriesFetch);
setCategories(categories);
}
updateData();
}, []);