![](/img/trans.png)
[英]Getting warning “Warning: Each child in a list should have a unique ”key“ prop.” when my component render
[英]Warning: Each child in a list should have a unique "key" prop. mapStateToProps
我是新手,我收到以下警告:
Warning: Each child in a list should have a unique "key" prop.
Check the render method of `TravelerStoriesPage`.
这是我的 TravelerStoriesPage:
const TravelerStoriesPage = ({
data,
dispatch,
currentPage,
totalPages,
loading,
location,
error,
scrollingDisabled,
intl,
}) => {
const urlParams = new URLSearchParams(location.search);
const tag = urlParams.get('tag');
let firstPost = null;
let secondRow = null;
let _3x3Grid = null;
const getImageLinkObject = data => ({
imageAltText: 'TravelerStories Post Image',
imageUrl: data.data.image.url,
linkProps: {
type: 'NamedLink',
name: 'TravelerStoriesPage',
to: {
pathname: `/stories/${data.uid}`,
},
},
text: (
<>
<HeadLine post={data} type="stories">
<RichText render={data.data.title} />
<RichText render={data.data.sub_title} />
</HeadLine>
<br />
<TagList post={data} />
</>
),
});
if (data.length) {
// First main post of the page
firstPost = {
linksPerRow: 1,
links: [getImageLinkObject(data[0])],
};
if (data.length > 1) {
// Second row of the page (first image)
secondRow = {
linksPerRow: 2,
links: [getImageLinkObject(data[1])],
};
}
// Second row of the page (second image)
if (data.length > 2) {
secondRow.links[1] = getImageLinkObject(data[2]);
}
// remaining 3 x 3 grid
if (data.length > 3) {
_3x3Grid = {
linksPerRow: 3,
links: data.slice(3).map(item => getImageLinkObject(item)), //////////////////USE OF MAP
};
}
}
useEffect(() => {
fetchTravelerStoriesPosts(dispatch, {
pageSize,
page: 1,
tag,
});
return () => {
dispatch(resetState());
};
}, [tag, dispatch]);
const fetchError = error ? (
<p className={css.error}>
<FormattedMessage id="BlogPage.fetchFailure" />
</p>
) : null;
const noResult =
!loading && !error && !data.length ? (
<p>
<FormattedMessage id="BlogPage.emptyResponse" />
</p>
) : null;
return (
<Page
scrollingDisabled={scrollingDisabled}
title={intl.formatMessage({ id: 'TravelerStoriesPage.title' })}
title={intl.formatMessage({ id: 'TravelerStoriesPage.description' })}
>
<LayoutSingleColumn>
<LayoutWrapperTopbar>
<TopbarContainer />
</LayoutWrapperTopbar>
<LayoutWrapperMain>
<div className={css.blogContainer}>
{fetchError}
{noResult}
{firstPost && <BlogThumbnailLinks {...firstPost} />}
{secondRow && <BlogThumbnailLinks {...secondRow} />}
{_3x3Grid && <BlogThumbnailLinks {..._3x3Grid} />}
{currentPage !== totalPages && (
<Button
rootClassName={css.loadMoreButton}
{...{
inProgress: loading,
disabled: false,
ready: false,
}}
onClick={() => {
if (loading) return;
fetchTravelerStoriesPosts(dispatch, {
tag,
pageSize,
page: noResult ? currentPage : currentPage + 1,
});
}}
>
{noResult ? (
<FormattedMessage id="BlogPage.refersh" />
) : (
<FormattedMessage id="BlogPage.loadMore" />
)}
</Button>
)}
{!loading && (
<div className={css.getInContactContainer}>
<h1 className={css.getInContactTitle}>
<FormattedMessage id="TravelerStoriesPage.getInContactTitle" />
</h1>
{replaceNewLines(
intl.formatMessage({ id: 'TravelerStoriesPage.getInContactDescription' }),
{ linkify: true }
)}
<a className={css.buttonLink} href={'mailto:stories@abcd.com'}>
<FormattedMessage id="TravelerStoriesPage.storiesButton" />
</a>
</div>
)}
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter>
<Footer />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
);
};
TravelerStoriesPage.defaultProps = {
...initialState,
};
const { bool, func, object, array, number } = PropTypes;
TravelerStoriesPage.propTypes = {
loading: bool.isRequired,
error: object,
totalPages: number,
currentPage: number,
data: array.isRequired,
dispatch: func.isRequired,
// from injectIntl
intl: intlShape.isRequired,
};
const mapStateToProps = state => {
const { data, totalPages, currentPage, loading, error } = state.TravelerStoriesPage;
return {
data,
error,
loading,
totalPages,
currentPage,
scrollingDisabled: isScrollingDisabled(state),
};
};
const mapDispatchToProps = dispatch => ({
dispatch,
});
const TravelerStoriesPageA = compose(
connect(mapStateToProps, mapDispatchToProps),
injectIntl
)(TravelerStoriesPage);
export default TravelerStoriesPageA;
博客缩略图链接
const BlogThumbnailLinks = props => {
const {
rootClassName,
className,
linksPerRow,
links,
heading,
subHeading,
headingRootClassName,
subHeadingRootClassName,
linkClassName,
linkRootClassName,
imageWrapperClassName,
} = props;
const classes = classNames(rootClassName || css.root, className);
const headingClasses = headingRootClassName || css.heading;
const subHeadingClasses = subHeadingRootClassName || css.subHeading;
return (
<div className={classes}>
{heading ? <h2 className={headingClasses}>{heading}</h2> : null}
{subHeading ? <p className={subHeadingClasses}>{subHeading}</p> : null}
<div className={css.links}>
{links.map((link, i) => (
<ThumbnailLink
key={`${link.imageUrl}${i}`}
linksPerRow={linksPerRow}
linkRootClassName={linkRootClassName}
className={linkClassName}
imageWrapperClassName={imageWrapperClassName}
{...link}
/>
))}
</div>
</div>
);
};
BlogThumbnailLinks.defaultProps = {
rootClassName: null,
className: null,
heading: null,
subHeading: null,
headingRootClassName: null,
subHeadingRootClassName: null,
imageWrapperClassName: null,
};
const namedLinkShape = shape({
type: oneOf(['NamedLink']).isRequired,
name: string.isRequired,
params: object,
to: shape({
search: string,
hash: string,
}),
});
const externalLinkShape = shape({
type: oneOf(['ExternalLink']).isRequired,
href: string.isRequired,
});
BlogThumbnailLinks.propTypes = {
rootClassName: string,
className: string,
linksPerRow: oneOf([1, 2, 3]).isRequired,
links: arrayOf(
shape({
imageUrl: string.isRequired,
imageAltText: string.isRequired,
linkProps: oneOfType([namedLinkShape, externalLinkShape]),
text: node.isRequired,
})
).isRequired,
heading: node,
subHeading: node,
headingRootClassName: string,
subHeadingRootClassName: string,
linkClassName: string,
linkRootClassName: string,
imageWrapperClassName: string,
};
export default BlogThumbnailLinks;
缩略图链接
const ThumbnailLink = props => {
const {
className,
rootClassName,
imageWrapperClassName,
linksPerRow,
imageUrl,
imageAltText,
linkProps,
text,
} = props;
const { type, name, params, to, href } = linkProps || {};
const classes = classNames(rootClassName || css.link, className, {
[css.link1Columns]: linksPerRow === 1,
[css.link2Columns]: linksPerRow === 2,
[css.link3Columns]: linksPerRow === 3,
});
const imageWrapperClasses = classNames(imageWrapperClassName || css.imageWrapper);
const LinkComponentProps = type === 'NamedLink' ? { name, params, to } : { href };
const LinkComponent = type === 'NamedLink' ? NamedLink : ExternalLink;
return (
<div className={classes}>
<LinkComponent {...LinkComponentProps}>
<div className={imageWrapperClasses}>
<div className={css.aspectWrapper}>
<img src={imageUrl} alt={imageAltText} className={css.image} />
</div>
</div>
</LinkComponent>
<div className={css.text}>{text}</div>
</div>
);
};
如果我像下面这样写 map function 是否正确?
链接:data.slice(3).map(item => getImageLinkObject(item), (key = item))
感谢您的所有帮助。 如果问题不符合要求,我将删除该问题,请通知我。
编辑:为跟踪 map function 的问题添加了 BlogThumbnailLinks 和 ThumbnailLink 组件代码。
如果@about14sheep 没有找到正确的位置,请尝试在getImageLinkObject 的文本元素中添加一个键。
编辑:尝试将密钥移动到片段
尝试这个:
const getImageLinkObject = data => ({
imageAltText: 'TravelerStories Post Image',
imageUrl: data.data.image.url,
linkProps: {
type: 'NamedLink',
name: 'TravelerStoriesPage',
to: {
pathname: `/stories/${data.uid}`,
},
},
text: (
<React.Fragment key={item.uid}>
<HeadLine post={data} type="stories">
<RichText render={data.data.title} />
<RichText render={data.data.sub_title} />
</HeadLine>
<br />
<TagList post={data} />
<\ React.Fragment>
),
});
所以这是一个常见的问题,当列出项目或使用.map 时反应。 但是,我很抱歉,但我在阅读您的代码时遇到了一些麻烦。 我想我找到了你需要添加密钥的地方,试试这个:
<LayoutWrapperMain>
<div className={css.blogContainer}>
{fetchError}
{noResult}
{firstPost && <BlogThumbnailLinks key="1" {...firstPost} />}
{secondRow && <BlogThumbnailLinks key="2" {...secondRow} />}
{_3x3Grid && <BlogThumbnailLinks key="3" {..._3x3Grid} />}
</div>
</LayoutWrapperMain>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.