Render parent element over x items in n array in React? - reactjs

I have a simple map function:
<div>
{
items.map((item, index)=>(
<p key={index}>{item}</p>
))
}
</div>
Which renders this HTML:
<div>
<p>1</p>
<p>2</p>
<p>3</p>
</div>
How can I insert a span over the first 2 elements so that I have?
<div>
<span>
<p>1</p>
<p>2</p>
</span>
<p>3</p>
</div>
Ive tried:
<div>
{
items.map((item, index)=>(
<>
{ index === 0 && <span> }
<p key={index}>{item}</p>
{ index === items.length - 1 && </span> }
</>
))
}
</div>
But I get an error:
Parsing error: Unexpected token

The error is due to this expressions:
// You must render children if you don't close JSX tag <span></span>
{ index === 0 && <span> }
// Same here, <span/>
{ index === items.length - 1 && </span> }
See JSX in Depth
Also, you trying to group two items under single span, doing it with a single map iteration may be over complicating things.
const items = [1, 2, 3];
const mapToParagraph = (item, index) => <p key={index}>{item}</p>;
const App = () => {
// or slice for more generics
const [first, second, ...rest] = items;
return (
<div>
<span style={{ color: `red` }}>
{[first, second].map(mapToParagraph)}
</span>
{rest.map(mapToParagraph)}
</div>
);
};

Try this, add slash at the end for span.
<div>
{[1, 2, 3, 4, 5, 6].map((item, index) => (
<>
{index === 0 && <span />}
<p key={index}>{item}</p>
{index === [1, 2, 3, 4, 5, 6].length - 1 && <span />}
</>
))}
</div>

EDITED
<div>
{
items.slice(0, 2).length > 0 && (
<span>
{ items.slice(0, 2).map((it, i) => (
<p key={i}>{it}</p>
))}
</span>
)
}
{
items.slice(2).map((it, i) => (
p>{it}</p>
))
}
</div>

Related

Unexpected token in JSX when using conditional statements

I'm trying to wrap every 3 items with a carousel component, but unfortunately as soon as I change the <p>start</p> <p>end</p> test element to the real component I get the following error:
"Unexpected token. Did you mean `{'}'}` or `&rbrace;`?",
const Alerts = (props) => {
const alerts = props.data;
let pager = 1;
return (
<ul className={styles.alerts}>
<Swiper
spaceBetween={30}
pagination={{
clickable: true,
}}
className="mySwiper"
>
{alerts.map((e, index) => (
<>
{pager % 4 === 0 || pager === 1 ? (<p>start</p>) : ""}
<div key={index} id={`alert-${index}`} className={styles.alert}>
<div className={styles.alert__content}>
<div className={styles.alert__content_top}>
<strong>
{e.token}
<a href={e.token_link}>
<i className="fa fa-external-link"></i>
</a>
</strong>
<span>{e.date}</span>
</div>
<div className={styles.alert__desc}>{e.description}</div>
</div>
<div className={styles.alert__type}>{e.type}</div>
</div>
{pager % 3 === 0 ? (<p>end</p>) : ""}
{pager === 3 ? (pager = 0) : ""}
{(pager = pager + 1)}
</>
))}
</Swiper>
</ul>
);
};
After the modification the lines looks like this:
{pager % 4 === 0 || pager === 1 ? (<SwiperSlide>) : ""}
and
{pager % 4 === 0 || pager === 1 ? (</SwiperSlide>) : ""}
What's wrong with the syntax? The numbers from the pager variable also visible on the site, maybe it's related to my syntax error?
The problem is <p>start</p> is correct JSX element. And <SwiperSlide> is just a part of element.
I'd recommend you convert your initial data array into a new array with small arrays with 3 elements in each. And go through it with two maps:
function splitToBulks(arr, bulkSize = 20) {
const bulks = [];
for (let i = 0; i < Math.ceil(arr.length / bulkSize); i++) {
bulks.push(arr.slice(i * bulkSize, (i + 1) * bulkSize));
}
return bulks;
}
const Alerts = (props) => {
const alerts = splitToBulks(props.data, 3);
let pager = 1;
return (
<ul className={styles.alerts}>
<Swiper
spaceBetween={30}
pagination={{
clickable: true,
}}
className="mySwiper"
>
{alerts.map((allerts3, index) => (
<SwiperSlide>
{allerts3.map((e, index) => (
<div key={index} id={`alert-${index}`} className={styles.alert}>
<div className={styles.alert__content}>
<div className={styles.alert__content_top}>
<strong>
{e.token}
<a href={e.token_link}>
<i className="fa fa-external-link"></i>
</a>
</strong>
<span>{e.date}</span>
</div>
<div className={styles.alert__desc}>{e.description}</div>
</div>
<div className={styles.alert__type}>{e.type}</div>
</div>
)}
</SwiperSlide>
)}
</>
))}
</Swiper>
</ul>
);
};

Conditionally add reference inside loop

I am conditionally assigning difference references (ref=) to elements inside a loop depending on index. I know the below is wrong (although works), how should I refactor?
const SortableItem = SortableElement(({ value, index }) => (
<div className="grid-item">
{index == 0 && (
<img className="image-item" src={value.imgUrl} ref={this.bigImage} />
)}
{index == 1 && (
<img className="image-item" src={value.imgUrl} ref={this.secondImage} />
)}
{index > 1 && <img className="image-item" src={value.imgUrl} />}
</div>
));
You can use ternary operator to assign a value based on index. Also make sure to use === when comparing values:
const SortableItem = SortableElement(({ value, index }) => {
const ref = index === 0 ? this.bigImage : index === 1 ? this.secondImage : null;
return (
<div className="grid-item">
<img className="image-item" src={value.imgUrl} ref={ref} />
</div>
);
});

How to Highlight the search keyword in the autosuggestion result list React JS

I have a search term, and based on the search term corresponding results are displayed. Trying to highlight the keyword which is search in the results list. But its giving me empty value.
this is what I tried:
render() {
const { label } = this.props;
const { searchResults, query } = this.state;
let textHighlighter;
return (
<div>
<form>
<input placeholder={label} ref={input => (this.search = input)} onChange={e => this.handleInputChange(e)} />
</form>
{searchResults &&
searchResults.results &&
searchResults.results.length > 0 && (
<ul>
{searchResults.results.map((result, i) => {
let searchKeywordIdx = result.indexOf(query);
if (searchKeywordIdx > -1) {
textHighlighter = [
result.substring(0, searchKeywordIdx),
<strong key={i}>
{result.substring(searchKeywordIdx, searchKeywordIdx + query.length).substring(searchKeywordIdx + query.length)}
</strong>
];
}
return <li key={result}>{textHighlighter}</li>;
})}
</ul>
)}
</div>
);
}
Note: this.state.query = "aaa"
Example JSON:
searchResults.results = [“aaa”, “aaa bbb”, “aaa ccc”]
So I need to highlight all the "aaa" in the results list.
https://codepen.io/smilesaayush/pen/NzgRmW?editors=0010
fixed a little problem in your textHighlighter.
return (
<div>
{searchResults &&
searchResults.results &&
searchResults.results.length > 0 && (
<ul>
{searchResults.results.map((result, i) => {
let searchKeywordIdx = result.indexOf(query);
if (searchKeywordIdx > -1) {
textHighlighter = [
result.substring(0, searchKeywordIdx),
<strong key={i}>
{result.substring(searchKeywordIdx, searchKeywordIdx + query.length)}
</strong>,
result.substring(searchKeywordIdx + query.length)
];
}
return <li key={result}>{textHighlighter}</li>;
})}
</ul>
)}
</div>
);

How do you toggle filtered list items inside of a div in React?

I'm building a React component that shows a filtered list of items in a div when users click on a button. Only the items within that div should be displayed on click. For some reason, though, the lists for every section are being toggled.
What am I doing wrong?
Here is my code: https://codesandbox.io/s/6yr0jzlpwn
Simply you can just define a specific value for each button then pass it to state
<div>
<h1>{this.state.title}</h1>
<div>
<button value={'1'} onClick={this.toggleWords}>肉</button>
{this.state.showWords === '1' && (
<ul>
{this.state.list.filter(function(word) {
return word[1] === "肉";
}).map(function (word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
<div>
<button value={'2'} onClick={this.toggleWords}>茶</button>
{this.state.showWords === '2' && (
<ul>
{this.state.list.filter(function(word) {
return word[1] === "茶";
}).map(function(word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
<div>
<button value={'3'} onClick={this.toggleWords}>日</button>
{this.state.showWords === '3' && (
<ul>
{this.state.list.filter(function(word) {
return word[0] === "日";
}).map(function(word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
</div>
In toggleWords function
toggleWords(e) {
const clickedButton = e.target.value;
if(clickedButton !== this.state.showWords){
this.setState({ showWords: clickedButton })
}else{
this.setState({ showWords: '' }) // handle close list if double click
}
}
In case if you want to expand two sections at once you need to change showWords state to be an array then use indexOf method to extend the section
<div>
<h1>{this.state.title}</h1>
<div>
<button value={'1'} onClick={this.toggleWords}>肉</button>
{this.state.showWords.indexOf('1') !== -1 && (
<ul>
{this.state.list.filter(function (word) {
return word[1] === "肉";
}).map(function (word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
<div>
<button value={'2'} onClick={this.toggleWords}>茶</button>
{this.state.showWords.indexOf('2') !== -1 && (
<ul>
{this.state.list.filter(function (word) {
return word[1] === "茶";
}).map(function (word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
<div>
<button value={'3'} onClick={this.toggleWords}>日</button>
{this.state.showWords.indexOf('3') !== -1 && (
<ul>
{this.state.list.filter(function (word) {
return word[0] === "日";
}).map(function (word) {
return <li>{word}</li>;
})}
</ul>
)}
</div>
</div>
Then in toggleWords function will delete the value from array if exist else it will add it
toggleWords(e) {
const clickedButton = e.target.value;
if (this.state.showWords.indexOf(clickedButton) !== -1) { // deleting the value from array if exist
this.setState(prevState => ({ showWords: this.state.showWords.filter(d => d !== clickedButton) }))
} else {
this.setState(prevState => ({ showWords: [...prevState.showWords, clickedButton] }))
}
}

Conditional Rendering of div in ReactJS?

I'm a newbie to ReactJS. I want to insert a condition in my code below so that when noPeopleText.length > 0, then only should the "no-people-row" div render, otherwise, I do not want this div rendering to the DOM if noPeopleText is an empty string or undefined.
What's the best way do add in a conditional for this?
const peopleMember = (props) => {
const { people, noPeopleText, title } = props;
const hasPeople = Boolean(people && people.length);
const peopleGroup = _.groupBy(people, (person, i) =>
Math.floor(i / 2)
);
return (
<div>
{ hasPeople &&
<SectionHeader
title={title}
/>
}
{ (hasPeople || noPeopleText) &&
<div className="c-team-members">
<div className="container">
{ hasPeople ? _.map(peopleMemberGroups, (members, i) => (
<div className="row" key={i}>
{members && members.map((member, j) => (
<TeamMember
key={j}
name={member.name}
/>
))
}
</div>
)) : //If noPeopleText.length > 0, render div below
<div className="row no-people-row">
<div className="col-xs-12" dangerouslySetInnerHTML={{__html: noPeopleText}} />
</div>
}
</div>
</div>
}
</div>
);
};
You already have conditional rendering in your code. For example:
{ hasPeople &&
<SectionHeader title={title} />
}
This will only render the component SectionHeader if hasPeople evaluates to true. If hasPeople evaluates to false then the whole expression would evaluate to false regardless of the second part of the &&. Thus it is never executed (rendered).
So do you want something like this?
const peopleMember = (props) => {
const { people, noPeopleText, title } = props;
const hasPeople = Boolean(people && people.length);
const peopleGroup = _.groupBy(people, (person, i) =>
Math.floor(i / 2)
);
return (
<div>
{ hasPeople &&
<SectionHeader
title={title}
/>
}
{ (hasPeople || noPeopleText) &&
<div className="c-team-members">
<div className="container">
{ hasPeople ? _.map(peopleMemberGroups, (members, i) => (
<div className="row" key={i}>
{members && members.map((member, j) => (
<TeamMember
key={j}
name={member.name}
/>
))
}
</div>
)) : (noPeopleText.length > 0) &&
<div className="row no-people-row">
<div className="col-xs-12" dangerouslySetInnerHTML={{__html: noPeopleText}} />
</div>
}
</div>
</div>
}
</div>
);
};
I think you can just use a nested ternary operator:
{ hasPeople
? //mapping
: noPeopleText.length > 0
? <div className="row no-people-row">
<div className="col-xs-12" dangerouslySetInnerHTML={{__html: noPeopleText}} />
</div>
: null
}

Resources