Today after watching a getting started course about React, I tried to code my very first React component, a "treeview" as the title says. It does work, but as a newbie I know there are many improvement that can be done.
It receives a data array like the one below:
[{
descp: 'UNE',
url: '/tree/une',
icon: 'icon-list',
children: [
{
descp: 'ATI',
url: '/tree/une/ati',
icon: 'icon-layers',
children: [{
descp: 'ATI-VC',
url: '/tree/une/ati/ativc',
icon: 'icon-pin',
children: []
},
{
descp: 'ATI-CMG',
url: '/tree/une/ati/aticmg',
icon: 'icon-pin',
children: []
}
]
},
{
descp: 'EMGEF',
url: '/tree/une/emgef',
icon: 'icon-layers',
children: []
},
{
descp: 'ECIE',
url: '/tree/une/ecie',
icon: 'icon-layers',
children: []
},
{
descp: 'GEYSEL',
url: '/tree/une/geysel',
icon: 'icon-layers',
children: []
}
]
}]
Maybe the data format can be improve as well but the real deal is the component:
const MyTree = (props) => {
const { list } = props;
return (
list.map((child,i)=>
<MyTreeNestedItems key={i} data={child} />
)
);
};
const MyTreeNestedItems = (props) => {
const { data } = props;
const createChildren = (list) => {
return (
list.map((child,i)=>
<MyTreeNestedItems key={i} data={child} />
)
)
}
let children = null;
if (data.children.length) {
children = createChildren(data.children);
}
return (
<li className="nav-item">
<a className="nav-link" href="#">
<span className={data.icon}></span> {" "}
{ data.descp }
</a>
<ul style={{listStyleType:"none"}}>{children}</ul>
</li>
);
};
render(<MyTree list={tree} />,document.querySelector('.js-mytree'));
Any tips would be gratefully appreciated.
1 Answer 1
You might notice that createChildren
is almost the same as MyTree
-- the only difference is that createChildren
takes its argument directly instead of as a props object. So with a small change we can remove createChildren
entirely:
const MyTreeNestedItems = (props) => {
const { data } = props;
let children = null;
if (data.children.length) {
children = <MyTree list={data.children} />;
}
return (
<li className="nav-item">
<a className="nav-link" href="#">
<span className={data.icon}></span> {" "}
{ data.descp }
</a>
<ul style={{listStyleType:"none"}}>{children}</ul>
</li>
);
}
This can be a bit more compact as so:
const MyTreeNestedItems = ({ data }) => {
return (
<li className="nav-item">
<a className="nav-link" href="#">
<span className={data.icon}></span> {" "}
{ data.descp }
</a>
<ul style={{listStyleType:"none"}}>
{data.children.length ? <MyTree list={data.children} /> : null}
</ul>
</li>
);
};
Currently this code renders invalid HTML -- it renders with a <li>
as the top level element but <li>
should only be contained in a <ul>
, <ol>
, or <menu>
.
Looking at the code so far, <MyTree>
is rendered in two places -- it may be tempting to just add another <ul>
around <MyTree list={tree} />
in the render()
call. But then we would have two occurrences of a structure like this: <ul><MyTree/></ul>
. It'd be easier to just move the <ul>
into the <MyTree/>
.
With that, the code now looks like this:
const MyTree = ({ list }) => {
return (
<ul style={{listStyleType:"none"}}>
{list.map((child,i)=> <MyTreeNestedItems key={i} data={child} />)}
</ul>
);
};
const MyTreeNestedItems = ({ data }) => {
return (
<li className="nav-item">
<a className="nav-link" href="#">
<span className={data.icon}></span> {" "}
{ data.descp }
</a>
{data.children.length ? <MyTree list={data.children} /> : null}
</li>
);
};
render(<MyTree list={tree} />, document.querySelector('.js-mytree'));
From here it's a matter of taste. Both of the components are just directly returning a value, so we can use short arrow function syntax now: (...) => (value)
instead of (...) => { return (value); }
Also, the components are now simple enough that I would just combine the two.
All in, that leaves us with this final version of the code:
const MyTree = ({ list }) => (
<ul style={{listStyleType:"none"}}>
{list.map(({icon, descp, children}, i) => (
<li className="nav-item" key={i}>
<a className="nav-link" href="#">
<span className={icon} /> {descp}
</a>
{children.length ? <MyTree list={children} /> : null}
</li>
))}
</ul>
);
render(<MyTree list={tree} />, document.querySelector('.js-mytree'));
-
\$\begingroup\$ Thanks a lot George, that's a great improvement of my code and explanation. Now in a few days I will be focusing on implementing collapse/expand functionality to the component. \$\endgroup\$Nestor– Nestor2019年08月21日 11:21:39 +00:00Commented Aug 21, 2019 at 11:21