折扣区域tabs的封装和切换
动态添加class 安装classnames
npm i classnames
子组件:
import PropTypes from "prop-types";
import React, { memo, useState } from "react";
import { TabsWrapper } from "./style";
import classNames from "classnames";
const SectionTabs = memo((props) => {
const { tabNames = [], tabClick } = props;
const [currentIndex, setCurrentIndex] = useState(0);
function itemClickHandle(index, name) {
setCurrentIndex(index);
// 子传父
tabClick(index, name);
}
return (
<TabsWrapper>
{tabNames.map((item, index) => {
return (
<div
onClick={(e) => itemClickHandle(index, item)}
key={index}
className={classNames("item", { active: index === currentIndex })}
>
{item}
</div>
);
})}
</TabsWrapper>
);
});
SectionTabs.propTypes = {
tabNames: PropTypes.array,
};
export default SectionTabs;
import styled from "styled-components";
export const TabsWrapper = styled.div`
display: flex;
.item {
box-sizing: border-box;
flex-basis: 120px;
flex-shrink: 0;
padding: 14px 16px;
margin-right: 16px;
border-radius: 3px;
font-size: 17px;
text-align: center;
border: 0.5px solid #d8d8d8;
white-space: nowrap;
cursor: pointer;
${(props) => props.theme.mixin.boxShadow};
&:last-child {
margin-right: 0;
}
&.active {
color: #fff;
background-color: ${(props) => props.theme.color.second};
}
}
`;
父组件:
// 从redux获取数据
const { goodPriceInfo, highScoreInfo, discountInfo } = useSelector(
(state) => ({
goodPriceInfo: state.home.goodPriceInfo,
highScoreInfo: state.home.highScoreInfo,
discountInfo: state.home.discountInfo,
}),
shallowEqual
);
// 数据的转换
const tabNames = discountInfo.dest_address?.map((item) => item.name);
const [name, setName] = useState("佛山");
// 传递给子组件的函数
const tabClickHandle = useCallback(function (index, item) {
setName(item);
}, []);
{/* 折扣数据 */}
<div className="discount">
<SectionHeader title={discountInfo.title} subtitle={discountInfo.subtitle} />
<SectionTabs tabNames={tabNames} tabClick={tabClickHandle} />
<SectionRooms roomList={discountInfo.dest_list?.[name]} itemWidth="33.333%" />
</div>
useState初始化值(只渲染一次)的问题(有值才渲染)
const initName = Object.keys(infoData.dest_list)[0];
const [name, setName] = useState(initName);
export function isEmptyO(obj) {
return !!Object.keys(obj).length;
}
组件仅渲染一次,第一次为空,第二次网络请求的数据,使用&&则第一次为空不渲染。
{isEmptyO(discountInfo) && <HomeSectionV2 infoData={discountInfo} />}
{isEmptyO(goodPriceInfo) && <HomeSectionV1 infoData={goodPriceInfo} />}
{isEmptyO(highScoreInfo) && <HomeSectionV1 infoData={highScoreInfo} />}
继续封装折扣区域组件:
子组件HomeSectionV2接收父组件Home传递过来的数据
import PropTypes from "prop-types";
import React, { memo, useCallback, useState } from "react";
import { SectionV2Wrapper } from "./style";
import SectionHeader from "@/components/section-header";
import SectionRooms from "@/components/section-rooms";
import SectionTabs from "@/components/section-tabs";
const HomeSectionV2 = memo((props) => {
// 从props获取数据
const { infoData } = props;
const initName = Object.keys(infoData.dest_list)[0];
const [name, setName] = useState(initName);
// 数据的转换
const tabNames = infoData.dest_address?.map((item) => item.name);
// 传递给子组件的函数
const tabClickHandle = useCallback(function (index, item) {
setName(item);
}, []);
return (
<SectionV2Wrapper>
<SectionHeader title={infoData.title} subtitle={infoData.subtitle} />
<SectionTabs tabNames={tabNames} tabClick={tabClickHandle} />
<SectionRooms roomList={infoData.dest_list?.[name]} itemWidth="33.333%" />
</SectionV2Wrapper>
);
});
HomeSectionV2.propTypes = {
infoData: PropTypes.object,
};
export default HomeSectionV2;
import styled from "styled-components";
export const SectionV2Wrapper = styled.div`
margin-top: 30px;
`;
父组件使用HomeSectionV2:
import React, { memo, useEffect } from "react";
import { shallowEqual, useDispatch, useSelector } from "react-redux";
import { HomeWrapper } from "./style";
import HomeBanner from "./c-cpns/home-banner";
import { fetchHomeDataAction } from "@/store/modules/home";
import HomeSectionV1 from "./c-cpns/home-section-v1";
import HomeSectionV2 from "./c-cpns/home-section-v2";
import { isEmptyO } from "@/utils";
const Home = memo(() => {
// 从redux获取数据
const { goodPriceInfo, highScoreInfo, discountInfo } = useSelector(
(state) => ({
goodPriceInfo: state.home.goodPriceInfo,
highScoreInfo: state.home.highScoreInfo,
discountInfo: state.home.discountInfo,
}),
shallowEqual
);
// 派发异步的事件:发送网络请求
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchHomeDataAction("传参数data"));
}, [dispatch]);
return (
<HomeWrapper>
<HomeBanner />
<div className="content">
<div className="good-price">
{isEmptyO(discountInfo) && <HomeSectionV2 infoData={discountInfo} />}
{isEmptyO(goodPriceInfo) && <HomeSectionV1 infoData={goodPriceInfo} />}
{isEmptyO(highScoreInfo) && <HomeSectionV1 infoData={highScoreInfo} />}
</div>
</div>
</HomeWrapper>
);
});
export default Home;
效果:
section-footer封装
icon:
import React, { memo } from "react";
import styleStrToObject from "./utils";
const IconArrow = memo(() => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 32 32"
aria-hidden="true"
role="presentation"
focusable="false"
style={styleStrToObject(
"display: block; fill: none; height: 12px; width: 12px; stroke: currentcolor; stroke-width: 5.33333; overflow: visible;"
)}
cursorshover="true"
>
<path fill="none" d="m12 4 11.3 11.3a1 1 0 0 1 0 1.4L12 28"></path>
</svg>
);
});
export default IconArrow;
SectionFooter:
import PropTypes from "prop-types";
import React, { memo } from "react";
import { FooterWrapper } from "./style";
import IconArrow from "@/assets/svg/icon_arrow";
const SectionFooter = memo((props) => {
const { name } = props;
let showMsg = "显示全部";
if (name) {
showMsg = `显示更多${name}房源`;
}
return (
<FooterWrapper color={name ? "#00848A" : "#000"}>
<div className="info">
<span className="text">{showMsg}</span>
<IconArrow />
</div>
</FooterWrapper>
);
});
SectionFooter.propTypes = {
name: PropTypes.string,
};
export default SectionFooter;
样式:
import styled from "styled-components";
export const FooterWrapper = styled.div`
display: flex;
margin-top: 10px;
.info {
display: flex;
align-items: center;
cursor: pointer;
font-size: 17px;
font-weight: 700;
color: ${(props) => props.color};
&:hover {
text-decoration: underline;
}
.text {
margin-right: 6px;
}
}
`;
V1,V2中使用:
效果: