吃完饭不好意思马上回家,写一种基于组合模式的Tab实现,有机会放到业务代码里:
import React, {
Children,
isValidElement,
cloneElement,
useState,
useRef,
} from "react";
import "./styles.css";
interface TabItemProps {
title: React.ReactNode;
id: string | number;
content: React.ReactNode;
}
interface TabContainerProps {
onChange?(id: string | number): void;
defaultRenderId?: string | number;
currentId?: string | number;
}
// @ts-ignore
const TabContainer: React.FC<React.PropsWithChildren<TabContainerProps>> = (
props
) => {
const { children, onChange, defaultRenderId, currentId: outerId } = props;
const [currentRenderId, updateCurId] = useState<string | number>(
defaultRenderId || ""
);
const contentRef = useRef<Map<number | string, React.ReactNode>>(new Map());
if (outerId && outerId !== currentRenderId) {
updateCurId(outerId);
}
return (
<>
<div>------------ 这里是Tab --------------</div>
<div className="tab-container">
{Children.map(children, (child) => {
if (isValidElement(child)) {
const childProps = child.props;
const id: string | number = Reflect.get(childProps, "id");
const content: React.ReactNode = Reflect.get(childProps, "content");
if (id && content) {
contentRef.current.set(id, content);
return cloneElement<any>(child, {
onChange: () => {
updateCurId(id);
onChange?.(currentRenderId);
},
});
} else {
return null;
}
}
return null;
})}
</div>
------------ 下面是内容区域 --------------
<div>{contentRef.current.get(currentRenderId)}</div>
</>
);
};
const TabItem: React.FC<TabItemProps> = (props) => {
const { title } = props;
const onChange = Reflect.get(props, "onChange") as () => void;
return (
<div onClick={() => onChange?.()} className="tab-item">
{title}
</div>
);
};
export const Tab: React.FC = () => {
const [curId, updateCurId] = useState<string | number>("");
const tempId = useRef<string | number>("");
return (
<>
<input
placeholder="current id"
onChange={(e) => {
tempId.current = e.target.value;
}}
/>
<button
children="格老子锁死"
onClick={() => {
updateCurId(tempId.current);
}}
/>
<TabContainer
onChange={(id) => {
console.log("current tab id", id);
}}
defaultRenderId={"solid"}
currentId={curId}
>
<TabItem
title="react"
id="react"
content={<div style={{ backgroundColor: "red" }}>learn react</div>}
/>
<TabItem
title="vue"
id="vue"
content={<div style={{ backgroundColor: "yellow" }}>learn vue</div>}
/>
<TabItem
title="solid"
id="solid"
content={<div style={{ backgroundColor: "blue" }}>learn solid</div>}
/>
asdasd asfasdf
<>123</>
</TabContainer>
</>
);
};
效果:
感觉应该升序生成id,下次再改