Please help me, I'm new in react. I'm rendering values from nested object. Each object has title and message property. Titles can be same. I want display messages under title. If title same as in previos object do not display it , only once. But in my case it displays after each message.
my object:
arrayOfMessages=[
{
title: 'cars',
message: 'toyota'
},
{
title: 'cars',
message: 'ford'
},
{
title: 'cars',
message: 'bmw'
},
{
title: 'bikes',
message: 'suzuki'
},
{
title: 'bikes',
message: 'bmw'
},
]
expected output:
title
message
message
message
title2
message
message
in my case:
title
message
title
message
title
message
title2
message
title2
message
<div>
{arrayOfMessages.map((item, idx) => {
const {
message,
title
} = item
return (
<div key={idx} className="message-content">
<p>{title}</p>
<p>{message}</p>
</div>
)
})}
</div>
One approach would be to group items of arrayOfMessages by the title field, to achieve the required rendering result by using the native .reduce() method:
const arrayOfMessages=[
{
title: 'cars',
message: 'toyota'
},
{
title: 'cars',
message: 'ford'
},
{
title: 'cars',
message: 'bmw'
},
{
title: 'bikes',
message: 'suzuki'
},
{
title: 'bikes',
message: 'bmw'
},
];
/* Use reduce() to group items of arrayOfMessages by title */
const groupedMessages = arrayOfMessages.reduce((groups, item) => {
/* Find group for the title of current item */
const group = groups.find(group => group.title === item.title);
/* If matching group found, add message of item to it's messages array */
if(group) {
group.messages.push(item.message);
}
/* Otherwise, add a new group for this title */
else {
groups.push({ title : item.title, messages : [] });
}
return groups;
}, [])
console.log(groupedMessages);
Using the code above, you could then revise your render() method to render the title once for each item category:
<div>
{ groupedMessages.map((group, idx0) => (<div key={idx0} className="message-content">
<h2>{ group.title }</h2>
{ group.messages.map((message, idx1) => (<p key={idx1}>{message}</p>)) }
</div>))
}
</div>
Hope that helps!
Your object is not well structured, should be like this:
arrayOfMessages=[
{
title:"cars",
description:["toyota","ford","bmw"]
},
{
title:"bike",
description:["suzuki","bmw"]
}
]
then you can implement your code like this :
{
arrayOfMessages.map((message, idx) => {
const { description, title } = message
return (
<div key={idx} className="message-content">
<p>{title}</p>
{description.map((desc, index) => (
<p key={index}>desc</p>
))}
</div>
)
})
}
You should make up your data first.
...
let renderMessages = {}
arrayOfMessages.forEach(message => {
if (!renderMessages[message.title]) {
renderMessages[message.title] = {
title: message.title,
messages: [message.message]
}
} else {
renderMessages[message.title].messages.push(message.message)
}
})
return (
<div>
{Object.keys(renderMessages).map(key => {
let msg = renderMessages[key]
return <div key={key} className="message-content">
<p>{msg.title}</p>
{msg.messages.map(content => <p key={content}>{content}</p>)}
</div>
})}
</div>
)
You could group the messages based on the title using reduce. Then loop through the entries of the merged object to get the desired format
const arrayOfMessages=[{title:'cars',message:'toyota'},{title:'cars',message:'ford'},{title:'cars',message:'bmw'},{title:'bikes',message:'suzuki'},{title:'bikes',message:'bmw'},]
const grouped = arrayOfMessages.reduce((acc, { title, message }) => {
acc[title] = acc[title] || [];
acc[title].push(message);
return acc;
}, {})
console.log(grouped)
Here's a live demo:
class Sample extends React.Component {
render() {
const arrayOfMessages = [{ title: 'cars', message: 'toyota' }, { title: 'cars', message: 'ford' }, { title: 'cars', message: 'bmw' }, { title: 'bikes', message: 'suzuki' }, { title: 'bikes', message: 'bmw' },]
const grouped = arrayOfMessages.reduce((acc, { title, message }) => {
acc[title] = acc[title] || [];
acc[title].push(message);
return acc;
}, {})
return (
<div>
{
Object.entries(grouped).map(([title, messages], i1) => (
<div key={i1}>
<h1>{title}</h1>
{ messages.map((message, i2) => (<p key={i2}>{message}</p>)) }
</div>)
)
}
</div>
);
}
}
// Render it
ReactDOM.render(
<Sample />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react"></div>
try this
render(){
//title watcher
let watcher = {};
return <div>
{arrayOfMessages.map((item, idx) => {
const {
message,
title
} = item
if(!watcher[title]){
//update watcher with title
//if watcher does not contain your title return title and message
watcher[title]=title;
return (
<div key={idx} className="message-content">
<p>{title}</p>
<p>{message}</p>
</div>
)
} else {
//if watcher found with title return your message
<div key={idx} className="message-content">
<p>{message}</p>
</div>
}
})}
</div>
}
Related
I receive the console error
ReferenceError: selectImage is not defined
at edit (index.js?fb2e:95)
I thought selectImage was defined in the following Gutenberg block:
/**
* Block dependencies
*/
import icon from './icon';
import './style.scss';
/**
* Internal block libraries
*/
const { __ } = wp.i18n;
const { registerBlockType } = wp.blocks;
const {
RichText,
MediaUpload,
BlockControls,
BlockAlignmentToolbar,
} = wp.editor
/**
* Register block
*/
export default registerBlockType(
'jsforwpblocks/heroblock',
{
title: __( 'Hero Block', 'jsforwpblocks' ),
description: __( 'Large block with hero image, text and buttons', 'jsforwpblocks' ),
category: 'common',
icon: {
background: 'rgba(254, 243, 224, 0.52)',
src: icon,
},
keywords: [
__( 'Banner', 'jsforwpblocks' ),
__( 'Call to Action', 'jsforwpblocks' ),
__( 'Message', 'jsforwpblocks' ),
],
attributes: {
message: {
type: 'array',
source: 'children',
selector: '.message-body',
},
blockAlignment: {
type: 'string',
default: 'wide',
},
imgUrl: {
type: 'string',
default: 'http://placehold.it/500'
}
},
getEditWrapperProps( { blockAlignment } ) {
if ( 'left' === blockAlignment || 'right' === blockAlignment || 'full' === blockAlignment ) {
return { 'data-align': blockAlignment };
}
},
selectImage(value) {
console.log(value);
setAttributes({
imgUrl: value.sizes.full.url,
})
},
edit: props => {
const { attributes: { message, blockAlignment }, className, setAttributes } = props;
const onChangeMessage = message => { setAttributes( { message } ) };
return (
<div className={ className }>
<BlockControls>
<BlockAlignmentToolbar
value={ blockAlignment }
onChange={ blockAlignment => setAttributes( { blockAlignment } ) }
/>
</BlockControls>
<RichText
tagName="div"
multiline="p"
placeholder={ __( 'Add your custom message', 'jsforwpblocks' ) }
onChange={ onChangeMessage }
value={ message }
/>
<div className="media">
<MediaUpload
onSelect={selectImage}
render={ ({open}) => {
return <img
src={attributes.imgUrl}
onClick={open}
/>;
}}
/>
</div>
</div>
);
},
save: props => {
const { attributes: { message, blockAlignment, imgUrl } } = props;
return (
<div
className={classnames(
`align${blockAlignment}`
)}
style={backgroundImage={imgUrl}}
>
<h2>{ __( 'Call to Action', 'jsforwpblocks' ) }</h2>
<div class="message-body">
{ message }
</div>
</div>
);
},
},
);
EDIT
If I move the function down into the edit function, the error disappears:
edit: props => {
const { attributes: { message, blockAlignment }, className, setAttributes } = props;
const onChangeMessage = message => { setAttributes( { message } ) };
function selectImage(value) {
console.log(value);
setAttributes({
imgUrl: value.sizes.full.url,
})
}
return (
<div className={ className }>
However, I receive a new error:
ReferenceError: attributes is not defined
at Object.render (index.js:101)
Line 101 is the last line of:
save: props => {
const { attributes: { message, blockAlignment, imgUrl } } = props;
return (
<div
className={classnames(
The updated code is here (pastebin.com).
Help appreciated.
I think you have to add className in save to your props object destructering like you did in edit:
const { attributes: { message, blockAlignment, imgUrl }, className } = props;
Two more things:
A few lines down you're using class, I'd change this to className as well
If I use classnames in a custom block I always add it to my imports:
import classnames from 'lodash/classnames'
Haven't actually tried if it would work without importing it.
I just quickly checked your block (the original version from pastebin - without my former edits) in my setup and I had the same error. But the error doesn't refer to save but to edit. What helped was adding imgUrl to your attributes destructering in edit (same as in save):
const { attributes: { message, blockAlignment, imgUrl }, className, setAttributes } = props;
and then only use imgUrl in your MediaUpload return src like that:
return <img src={imgUrl} onClick={open} />;
As an addition to your second file on Pastebin try the following replacement for your save function:
save: props => {
const { attributes: { message, blockAlignment, imgUrl } } = props;
const divStyle = {
backgroundImage: 'url(' + imgUrl + ')',
};
return (
<div
className={classnames(
`align${blockAlignment}`
)}
style={divStyle}
>
<h2>{ __( 'Call to Action', 'jsforwpblocks' ) }</h2>
<div className="message-body">
{ message }
</div>
</div>
);
},
And the classnames import actually only worked like that (but might depend on how you set your dependencies):
import classnames from 'classnames'
I have such situation:
There is an array:
const arr = [
{
id: 0,
title: 'a',
status: false
},
{
id: 1,
title: 'a',
status: false
},
{
id: 2,
title: 'a',
status: false
},
]
Then I use this array in my React component to get values, but here I also need to change status to true or false in current element (not for all objects)
arr.map(el => {
return (
<div key={el.id}>
<div onClick={() => !status}>{div.title}</div> // here
</div>
)
})
So how can I make this?
How about simply (if you have the access to the arr variable) you have an option of getting an index of the array as the 2nd parameter in the map function.
arr.map((el, ix) => {
return (
<div key={el.id}>
<div onClick={() => {
arr[ix].status = !arr[ix].status
}}>{div.title}</div> // here
</div>
)
})
or pull it out in a function (better way) as
function handleClick (ix) {
const currentStatus = arr[ix].status
arr[ix].status = !currentStatus
}
arr.map((el, ix) => {
return (
<div key={el.id}>
<div onClick={() => handleClick(ix)}>{div.title}</div> // here
</div>
)
})
EDIT: Didn't see it was a reactJS, my bad, in that case the best case is to manage it with state.
You can try:
const handleClick = (el) => () => {
el.status = !el.status
}
arr.map(el => {
return (
<div key={el.id}>
<div onClick={handleClick(el)}>{div.title}</div> // here
</div>
)
})
Maintain array in react state;
const [arr,setArr]=useState([
{
id: 0,
title: 'a',
status: false
},
{
id: 1,
title: 'a',
status: false
},
{
id: 2,
title: 'a',
status: false
},
])
Write a function to handle the change in state
const changeStatus =id=>{
const newArr = arr.map((el,index)=>{
if(el.id===id){
return {...el,status:!el.status}
}
else return;
})
setArr(newArr)
}
call this function on onClick.
arr.map(el => {
return (
<div key={el.id}>
<div onClick={() => changeStatus(el.id)}>{div.title}</div> // here
</div>
)
})
The following code outputs two separate form sections. Everything works fine but I have two for() loops that are similar and two map() functions that are also similar. I want to learn how to write a nested script with the for() loop and map() function so that I can add more properties to my state.program object and my form will update automatically without me having to add another for() or map().
Basically I'm trying to loop through the object, create arrays to map to my component props. I'm thinking about this the wrong way?
I hope the description of my problem makes sense. Here are the React Components...
class CreateProgram extends Component {
state = {
program: {
description: {
title: {
label: 'Program title',
value: ''
},
category: {
label: 'Program category',
value: ''
}
},
location: {
location_title: {
label: 'location title',
value: ''
},
location_coor: {
label: 'location coordinates',
value: null
}
}
}
};
render() {
return <Program items={this.state.program} />;
}
}
export default CreateProgram;
class Program extends Component {
render() {
const { items } = this.props;
const descriptionArray = [];
const locationArray = [];
for (let key in items.description) {
descriptionArray.push({
id: key,
value: items.description[key]
});
}
for (let key in items.location) {
locationArray.push({
id: key,
value: items.location[key]
});
}
return (
<>
<div className="form-section">
{descriptionArray.map(element => (
<Input
label={element.value.label}
value={element.value.value}
changed={event =>
changed(event, element.id, 'program', 'description')
}
/>
))}
</div>
<div className="form-section">
{locationArray.map(element => (
<Input
label={element.value.label}
value={element.value.value}
changed={event =>
changed(event, element.id, 'program', 'location')
}
/>
))}
</div>
</>
);
}
}
export default Program;
You could simplify the parent state:
state = {
description: {
title: {
label: "Program title",
value: ""
},
category: {
label: "Program category",
value: ""
}
},
location: {
location_title: {
label: "location title",
value: ""
},
location_coor: {
label: "location coordinates",
value: null
}
}
};
Then pass these separately to the child component:
render() {
return (
<Program
description={this.state.description}
location={this.state.location}
/>
);
};
And in the child component use Object.entries returned array to map each element:
Object.entries(this.props.description).map(([key, value]) => (
<Input
key={key}
label={value.label}
value={value.value}
changed={event => changed(event, key, "program", "description")}
/>
));
Object.entries(this.props.location).map(([key, value]) => (
<Input
key={key}
label={value.label}
value={value.value}
changed={event => changed(event, key, "program", "location")}
/>
));
Expected effect:
click button -> call function save -> pass object p to function update
update second object{a: 'purple', desc: 'grt', date: '12 -10-2019 '} in colors array, which is in theproducts array
Before update: {a: 'purple', desc: 'grt', date: '12 -10-2019 '}
After update: {a: 'violet', desc: 'gt', date: '12 -12-1980 '}
Error in console.log:
Uncaught TypeError: this.props.product.colors.map is not a function
App
class App extends Component {
constructor (props) {
super(props);
this.state = {
products: [
{
colors: [{a:'orange', desc: 'grtrt', date: '02-12-2019'}, {a:'purple', desc: 'grt', date: '12-10-2019'}]
desc: 'gfgfg',
},
{
colors: [{a:'black', desc: 'g', date: '12-12-2019'}, {a: 'white', {a:'black', desc: 'grtrt', date: '12-12-2119'}, }, {a:'gray', desc:'', date: '01-01-2000'}],
desc: 'gfgfgfg',
}
],
selectProductIndex: 0 //It is first object in products array
index: 1 //It is second object in colors array
}
}
update = (item) => {
const {selectProductIndex} = this.state;
this.setState(prevState => {
return {
products: [
...prevState.products.slice(0, selectProductIndex),
Object.assign({}, prevState.products[selectProductIndex], {colors: item}),
...prevState.products.slice(selectProductIndex + 1)
]
};
});
}
render () {
return (
<div>
<Items
product={this.state.products[this.state.selectProductIndex]}
update = {this.update}
/>
</div>
)
}
Items
class Items extends Component {
render () {
return (
<ul>
{
this.props.product.colors
.map((item, index) =>
<Item
key= {index}
index = {index}
time = {item}
update = {this.props.update}
/>
)
}
</ul>
</div>
);
}
}
Item
class Item extends Component {
save = () => {
const p = {
a:'violet', desc: 'gt', date: '12-12-1980'
}
this.props.update(p)
}
render() {
return (
<div>
<button onClick={this.save}>Save</button>
</div>
)
}
}
You need to pass the index of the colors item and then update it accordingly
class Item extends Component {
save = () => {
const p = {
a:'violet', desc: 'gt', date: '12-12-1980'
}
this.props.update(p, this.props.index)
}
render() {
return (
<div>
<button onClick={this.save}>Save</button>
</div>
)
}
}
and then in the topmost parent
update = (item, colorIndex) => {
const {selectProductIndex} = this.state;
this.setState(prevState => {
return {
products: [
...prevState.products.slice(0, selectProductIndex),
Object.assign({}, prevState.products[selectProductIndex], {colors: prevState.products[selectProductIndex].colors.map((it,idx) => {
if(idx === colorsIndex) { return item}
return it;
})}),
...prevState.products.slice(selectProductIndex + 1)
]
};
});
}
Working demo
const { Component } = React;
class App extends Component {
constructor (props) {
super(props);
this.state = {
products: [
{
colors: [{a:'orange', desc: 'grtrt', date: '02-12-2019'}, {a:'purple', desc: 'grt', date: '12-10-2019'}],
desc: 'gfgfg',
},
{
colors: [{a:'black', desc: 'g', date: '12-12-2019'}, {a:'black', desc: 'grtrt', date: '12-12-2119'}, {a:'gray', desc:'', date: '01-01-2000'}],
desc: 'gfgfgfg',
}
],
selectProductIndex: 0,
index: 1
}
}
update = (item, colorIndex) => {
const {selectProductIndex} = this.state;
this.setState(prevState => {
return {
products: [
...prevState.products.slice(0, selectProductIndex),
Object.assign({}, prevState.products[selectProductIndex], {colors: prevState.products[selectProductIndex].colors.map((it,idx) => {
if(idx === colorIndex) { return item}
return it;
})}),
...prevState.products.slice(selectProductIndex + 1)
]
};
});
}
render () {
return (
<div>
<Items
product={this.state.products[this.state.selectProductIndex]}
update = {this.update}
/>
</div>
)
}
}
class Items extends Component {
render () {
return (
<ul>
{
this.props.product.colors
.map((item, index) =>
<Item
key= {index}
index = {index}
time = {item}
update = {this.props.update}
/>
)
}
</ul>
);
}
}
class Item extends Component {
save = () => {
const p = {
a:'violet', desc: 'gt', date: '12-12-1980'
}
this.props.update(p, this.props.index)
}
render() {
return (
<div>
<pre>{JSON.stringify(this.props.time)}</pre>
<button onClick={this.save}>Save</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app" />
Here is a sample function called index() that I use to return an index of headers from a Markdown file. As you can see, with only 3 levels of headers, its quickly getting out of hand.
I tried a variety recursion ideas, but ultimately failed at all attempts to compile in create-react-app.
Could anyone offer any guidance as to how to clean this up and maybe even allow for an infinite/greater levels of nesting?
An array is stored in this.state.index which resembles the array below.
[
{
label: 'Header title',
children: [
{
label: 'Sub-header title'
children: [...]
}
]
}
]
Then that data is used in the function below to generate a ul index.
index() {
if ( this.state.index === undefined || !this.state.index.length )
return;
return (
<ul className="docs--index">
{
this.state.index.map((elm,i=0) => {
let key = i++;
let anchor = '#'+elm.label.split(' ').join('_') + '--' + key;
return (
<li key={key}>
<label><AnchorScroll href={anchor}>{elm.label}</AnchorScroll></label>
<ul>
{
elm.children.map((child,k=0) => {
let key = i+'-'+k++;
let anchor = '#'+child.label.split(' ').join('_') + '--' + key;
return (
<li key={key}>
<label><AnchorScroll href={anchor}>{child.label}</AnchorScroll></label>
<ul>
{
child.children.map((grandchild,j=0) => {
let key = i+'-'+k+'-'+j++;
let anchor = '#'+grandchild.label.split(' ').join('_') + '--' + key;
return (
<li key={key}>
<label><AnchorScroll href={anchor}>{grandchild.label}</AnchorScroll></label>
</li>
);
})
}
</ul>
</li>
);
})
}
</ul>
</li>
);
})
}
</ul>
);
}
Like I said, this is a mess! I'm new to React and coding in general so sorry if this is a silly question.
Seems like you want a recursive call of a component.
For example, component Item will render it self based on a condition (existence of the children array).
Running example:
const data = [
{
label: 'Header title',
children: [
{
label: 'Sub-header title',
children: [
{ label: '3rd level #1' },
{
label: '3rd level #2',
children: [
{ label: 'Level 4' }
]
}
]
}
]
}
]
class Item extends React.Component {
render() {
const { label, children } = this.props;
return (
<div>
<div>{label}</div>
<div style={{margin: '5px 25px'}}>
{children && children.map((item, index) => <Item key={index} {...item} />)}
</div>
</div>
)
}
}
const App = () => (
<div>
{data.map((item, index) => <Item key={index} {...item} />)}
</div>
);
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Edit
Just for completeness, i added another example but this time we handle the isOpen property outside the Item's local component state, in a higher level.
This can be easily moved to a redux reducer or any other state management library, or just let a higher level component like the App in this case manage the changes.
So, to handle changes of a recursive component you would probably write a recursive handler:
const data = [
{
label: 'Header title',
id: 1,
children: [
{
label: 'Sub-header title',
id: 2,
children: [
{ label: '3rd level #1', id: 3 },
{
label: '3rd level #2',
id: 4,
children: [
{ label: 'Level 4', id: 5 }
]
}
]
},
]
},
{
label: 'Header #2',
id: 6,
children: [
{ label: '2nd level #1', id: 7, },
{
label: '2nd level #2',
id: 8,
children: [
{ label: 'Level 3', id: 9 }
]
}
]
}
]
class Item extends React.Component {
toggleOpen = e => {
e.preventDefault();
e.stopPropagation();
const {onClick, id} = this.props;
onClick(id);
};
render() {
const { label, children, isOpen, onClick } = this.props;
return (
<div className="item">
<div
className={`${children && "clickable"}`}
onClick={children && this.toggleOpen}
>
<div
className={`
title-icon
${isOpen && " open"}
${children && "has-children"}
`}
/>
<div className="title">{label}</div>
</div>
<div className="children">
{children &&
isOpen &&
children.map((item, index) => <Item key={index} {...item} onClick={onClick} />)}
</div>
</div>
);
}
}
class App extends React.Component {
state = {
items: data
};
toggleItem = (items, id) => {
const nextState = items.map(item => {
if (item.id !== id) {
if (item.children) {
return {
...item,
children: this.toggleItem(item.children, id)
};
}
return item;
}
return {
...item,
isOpen: !item.isOpen
};
});
return nextState;
};
onItemClick = id => {
this.setState(prev => {
const nextState = this.toggleItem(prev.items, id);
return {
items: nextState
};
});
};
render() {
const { items } = this.state;
return (
<div>
{items.map((item, index) => (
<Item key={index} {...item} onClick={this.onItemClick} />
))}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
.item {
padding: 0 5px;
}
.title-icon {
display: inline-block;
margin: 0 10px;
}
.title-icon::before {
margin: 12px 0;
content: "\2219";
}
.title-icon.has-children::before {
content: "\25B6";
}
.title-icon.open::before {
content: "\25E2";
}
.title-icon:not(.has-children)::before {
content: "\2219";
}
.title {
display: inline-block;
margin: 5px 0;
}
.clickable {
cursor: pointer;
user-select: none;
}
.open {
color: green;
}
.children {
margin: 0 15px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
I don't have good test data, but try this out:
index() {
if ( this.state.index === undefined || !this.state.index.length ) {
return null;
}
const sections = this.state.index.map(this.handleMap);
return (
<ul>
{sections}
</ul>
)
}
handleMap(elm, i) {
const anchor = '#'+elm.label.split(' ').join('_') + '--' + i;
return (
<li key={i}>
<label><AnchorScroll href={anchor}>{elm.label}</AnchorScroll></label>
<ul>
{
elm.children.map(this.handleMapChildren)
}
</ul>
</li>
);
}
handleMapChildren(child, i) {
let anchor = '#'+child.label.split(' ').join('_') + '--' + i;
return (
<li key={i}>
<label><AnchorScroll href={anchor}>{child.label}</AnchorScroll></label>
<ul>
{
child.children.map(this.handleMapChildren)
}
</ul>
</li>
);
}