本文同时发布于掘金,地址:https://juejin.im/post/6844903848335638535
本文译自:30-seconds-of-react。React 30 秒速学:全篇中文翻译、学习,地址:30-seconds-of-react-zh_CN-umi,所有案例进行分析、注释、上线。
树形组件
实现一个可折叠、无限层级、支持数组和对象的树组件。
- 使用对象解构来设置某些传入属性的默认值。
- 使用传入的
toggled
属性来确定内容的初始状态(折叠/展开)。 - 使用
React.setState()
hook 来创建isToggled
状态变量,并在最初为它赋予传入的toggled
的值。 - 返回一个
<div>
来包装组件的内容和用于改变组件的isToggled
状态的<span>
元素。 - 根据
data
上的isParentToggled
,isToggled
,name
和Array.isArray()
确定组件的外观。 - 对于
data
中的每个子节点,确定它是对象还是数组,并递归渲染子树。 - 否则,使用适当的样式渲染一个
<p>
元素。
样式:
/* 树节点的基本样式 */
.tree-element {
margin: 0;
position: relative;
}
div.tree-element:before {
content: '';
position: absolute;
top: 24px;
left: 1px;
height: calc(100% - 48px);
border-left: 1px solid gray;
}
/* 切换显示、隐藏的按钮元素 */
.toggler {
position: absolute;
top: 10px;
left: 0px;
width: 0;
height: 0;
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
border-left: 5px solid gray;
cursor: pointer;
}
.toggler.closed {
transform: rotate(90deg);
}
/* 隐藏节点内容 */
.collapsed {
display: none;
}
树组件,组件内容较多,在学习过程中我也对其中的内容,分别进行了注释:
import styles from "./TreeView.css";
function TreeView({
// 使用对象解构来设置某些传入属性的默认值
data,
toggled = true, // 折叠按钮,是否处于折叠状态
name = null, // 当前属性名,如果子元素是对象显示
isLast = true, // 是否最后一个
isChildElement = false, // 是否子元素
isParentToggled = true // 是否被父节点折叠
}) {
const [isToggled, setIsToggled] = React.useState(toggled);
return (
<div
style={{ marginLeft: isChildElement ? 16 : 4 + "px" }}
// 如果父折叠就隐藏
className={isParentToggled ? styles["tree-element"] : styles.collapsed}
>
{/* 折叠按钮,点击设置反状态 */}
<span
className={
isToggled ? styles.toggler : `${styles.toggler} ${styles.closed}`
}
onClick={() => setIsToggled(!isToggled)}
/>
{name ? <strong> {name}: </strong> : <span> </span>}
{/* 开始符 */}
{Array.isArray(data) ? "[" : "{"}
{/* 子元素被折叠 */}
{!isToggled && "..."}
{/* 渲染对象的子元素 */}
{Object.keys(data).map((v, i, a) =>
// 是对象,递归调用自身
typeof data[v] == "object" ? (
<TreeView
data={data[v]}
key={i}
isLast={i === a.length - 1}
// 子元素的属性名,对象需要显示属性名,数组不显示
name={Array.isArray(data) ? null : v}
isChildElement
isParentToggled={isParentToggled && isToggled}
/>
) : ( // 不是对象,显示内容即可
<p
key={i}
style={{ marginLeft: 16 + "px" }}
className={isToggled ? styles["tree-element"] : styles.collapsed}
>
{Array.isArray(data) ? "" : <strong>{v}: </strong>}
{data[v]}
{i === a.length - 1 ? "" : ","}
</p>
)
)}
{/* 结束符 */}
{Array.isArray(data) ? "]" : "}"}
{/* 不是最后元素,加个逗号 */}
{!isLast ? "," : ""}
</div>
);
}
例子
export default function() {
let data = {
lorem: {
ipsum: "dolor sit",
amet: {
consectetur: "adipiscing",
elit: [
"duis",
"vitae",
{
semper: "orci"
},
{
est: "sed ornare"
},
"etiam",
["laoreet", "tincidunt"],
["vestibulum", "ante"]
]
},
ipsum: "primis"
}
};
return <TreeView data={data} name="data" />;
}
ps: