How to map i and i+1 values of a javascript array? - javascript

I would like to use a map to display the ith value and the i+1th value inside a javascript map to display a set of two cards inside a carousel.
The following is the code I am using, which I would like to modify to get the desired result:
{testimonialContent.map((testimonial, i) => {
return (
<Carousel.Item className={classes.carousel_item}>
<div className={classes.root}>
<div className={classes.testimonial_card}>
{testimonial[i]}
</div>
{/* Second card */}
<div className={classes.second_card}>
{testimonial[i+1]}
</div>
</div>
</Carousel.Item>
Any help or suggestion is appreciated. Thanks.

You can use reduce() to combine data.
NOTE: (index % 2 === 0) is where you specify your nth child (eg: 2 in this case)
Later in your render method use destructuring in map().
const sampleData = ['first', 'second', 'third', 'fourth', 'fifth']
class App extends React.Component {
render() {
console.log("rendered");
const edited = [].concat(sampleData).reduce((unique, value, index) => {
if (index % 2 === 0) {
return [...unique, [value]];
}
unique[unique.length - 1].push(value);
return unique;
}, [])
return (
<div>
{edited.map(([item1, item2]) =>
<div className='block'>
{item1 && <div className='odd'>{item1}</div>}
{item2 && <div className='even'>{item2}</div>}
</div>
)}
</div>
);
}
}
// Render it
ReactDOM.render(
<App />,
document.getElementById("react")
);
.block {
border: 1px solid gray;
}
.odd {
color: red;
}
.even {
color: blue;
}
<div id="react"></div>
<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>

use the entire array not the current iteration
Testimonial in each iteration of the map is a single object. Whereas testimonialContent is the entire array.
{testimonialContent.map((testimonial, i) => {
return (
<Carousel.Item className={classes.carousel_item}>
<div className={classes.root}>
<div className={classes.testimonial_card}>
{testimonial[i]}
</div>
{/* Second card */}
<div className={classes.second_card}>
{testimonialContent[i+1]} // this is the line to change
</div>
</div>
</Carousel.Item>
But something feels very off here. I think you might have an x y problem
Why would you want to use the next iteration as the current iteration? The last item will fail because it doesn't exist and the value of the first testimonialContent will never be shown because you're always starting from index 1.
I would rethink what you're doing here and maybe take another approach.

testimonial is not an array, but the current iterated item in that array. For the "next" item you can use the array testimonialContent.
From the comment I learn that you want to actually produce n/2 rows, so that each item only occurs once. In that case using a plain .map() is not good as it will generate as many rows, and you only need half that number of rows. Instead, you could generate an array with half the number of rows (rounded upwards) with Array.from and use the callback argument of that method.
Array.from({length: (testimonialContent.length+1)>>1}, (_, i) => {
return (
<Carousel.Item className={classes.carousel_item}>
<div className={classes.root}>
<div className={classes.testimonial_card}>
{testimonialContent[i*2]}
</div>
{/* Second card */}
<div className={classes.second_card}>
{testimonialContent[i*2+1] || ""}
</div>
</div>
</Carousel.Item>
)
})

Refer to the testimonial instance in your first div and to the element on the i-th +1 palce in your testimonialContent array in your seccond div.
{testimonialContent.map((testimonial, i) => {
return (
<Carousel.Item className={classes.carousel_item}>
<div className={classes.root}>
<div className={classes.testimonial_card}>
{testimonial}
</div>
{/* Second card */}
<div className={classes.second_card}>
{testimonialContent[i+1]}
</div>
</div>
</Carousel.Item>

Related

in react when I remove a dynamic input, the input does not show the right value on the browser

I'm new in React and i'm learning to use it with hooks.
I tried to put dynamics inputs which works so far but I have a display problem.
If I delete the last input, no problem but if I delete others inputs than the last one then the correct values does not show on the browser.
For example I add 2 inputs.
first one titled "one" and second titled "two".
If I delete "one", the remain input on screen shows "one" or it should show "two".
However, in the array where I collect the inputs infos, there is the correct remaining input.
(see screenshot).
How can I do to show the correct title in the input on the browser ?
const [titleSelected, setTitleSelected] = useState(true);
const [questionType, setQuestionType] = useState([]);
const [questionsTitle, setQuestionsTitle] = useState([]);
{questionType ? (
questionType.map((questions, index) => {
return (
<div key={index} className="questions">
<div className={questions === "texte" ? "orange" : "red"}>
<span>{questions === "texte" ? "1" : "2"}</span>
<img src={Minus} alt="" />
<img
src={questions === "texte" ? FileWhite : StarWhite}
alt=""
/>
</div>
<input
type="text"
placeholder="Ecrivez votre question"
onChange={(event) => {
let tab = [...questionsTitle];
// si index de l'objet existe on update index de l'objet par index sinon on push le nouvel objet
let tabIndex = tab.findIndex(
(element) => element.index === index
);
if (tabIndex !== -1) {
tab[tabIndex].type = questionType[index];
tab[tabIndex].title = event.target.value;
} else {
tab.push({
index: index,
type: questionType[index],
title: event.target.value,
});
}
setQuestionsTitle(tab);
}}
></input>
<div>
<img src={ChevronUp} alt="move up" />
<img src={ChevronDown} alt="move down" />
<img
src={SmallTrash}
alt="delete question"
onClick={() => {
let tab = [...questionType];
tab.splice(index, 1);
setQuestionType(tab);
let tabTitle = [...questionsTitle];
tabTitle.splice(index, 1);
setQuestionsTitle(tabTitle);
}}
/>
</div>
</div>
);
})
) : (
<div></div>
)}
<div className="questionType">
<div
className="addText"
onClick={() => {
let tab = [...questionType];
tab.push("texte");
setQuestionType(tab);
}}
>
<img src={File} alt="" />
<p>Ajouter une question "Texte"</p>
</div>
<div
className="addNote"
onClick={() => {
let tab = [...questionType];
tab.push("note");
setQuestionType(tab);
}}
>
<img src={Star} alt="" />
<p>Ajouter une question "Note"</p>
</div>
</div>
screenshot
Issues
You are mutating the array you are mapping so don't use the array index as the react key. If you remove the ith element all the elements shift up, but the keys remain unchanged and react bails on rerendering.
Lists & Keys
questionType.map((questions, index) => (
<div key={index} className="questions">
...
</div>
)
The array index as a React key doesn't "stick" to the element object it "identifies".
You've also some state object mutation occurring.
tab[tabIndex].type = questionType[index];
tab[tabIndex].title = event.target.value;
This is masked by the shallow copy let tab = [...questionsTitle];.
Solution
Select a react key that is unique among siblings and related to the elements, like an id.
Since you enclose the index when adding new elements to the array I think you can resolve the key issue by simply using the saved index.
questionType.map((questions, index) => (
<div key={questionsTitle[index].index} className="questions">
...
</div>
)
This may be a little confusing so I suggest changing the property to id.
questionType.map((questions, index) => (
<div key={questionsTitle[index].id} className="questions">
...
<input
...
onChange={event => {
...
tab.push({
id: index,
type: questionType[index],
title: event.target.value,
});
...
}}
/>
...
</div>
)
A further suggestion is to avoid using the array index at all. The following code can quickly get out of sync when the array index being mapped doesn't align to the saved index in the element.
let tabIndex = tab.findIndex(element => element.index === index);
Generate id's for your elements and use that to determine if elements should be updated or appended. When updating make sure to shallow copy the array and then also shallow copy the element object being updated.

How to separate items from array?

I'm making weather app for 7 days and I need to create abillity for users to open some of days to see more information about weather of current day. So, it means that every item choosen by user has to contain unique id (i don't know could I use in this situation index instead of id) to show information only about some of day. So before this I had some code:
const DailyWeatherData = ({dailyData, isLoading}) => {
const getWeatherStatistic = (value0,value1) => {
if(dailyData && Array.isArray(dailyData.daily)){
return (
<div className="col-lg-3 box-daily-weather">
<NavLink to={'/WeatherDayStat'}>
{setDay([value1])}
<div className="temp-day">{Math.ceil(dailyData.daily[value0].temp.day)}°C</div>
<div className="feels-like">Feels like: {Math.ceil(dailyData.daily[value0].feels_like.day)}°C</div>
<div className="daily-weather-condition">Conditions: {dailyData.daily[value0].weather.map(e => e.main)}</div>
</NavLink>
</div>
)
}else {
return isLoading === true ? <Preloader /> : null
}
}
const setDay = (param) => {
return checkCod ? null : setCurrentDate(new Date(),param)
}
return (
<div>
<div className="daily-weather-container">
{checkCod ? null : <div className="daily-title">Daily Weather</div>}
<div className='row scrolling-wrapper justify-content-center'>
{getWeatherStatistic(1,1)}
{getWeatherStatistic(2,2)}
{getWeatherStatistic(3,3)}
{getWeatherStatistic(4,4)}
{getWeatherStatistic(5,5)}
{getWeatherStatistic(6,6)}
</div>
</div>
</div>
)
}
export default DailyWeatherData
In function getWeatherStatistic you can see 2 arguments, value0 - doesn't matter here, value1 - using to show only 1st-6 object, because array which i have got from ajax request contains more days(10) but I need to show only 6 of them. And most importantly, each of them is separate. Logically I'd use map, but It shows all items, so I can use slice but it also shows 6 items in 1 column.
The next problem, this array has not contain parameter like ID, that's why I can't add to NavLink id. If there were ID, I would make something like that dailyData.daily.map(p => <NavLink to={'/WeatherDayStat' + p.id}>)
Also, just in case, add the state code (daily - array which I need):
So I have 2 questions:
How to show only 6 days from array?
How to add unique ID to every Item from array?
Making something like this:
//making new array with data I need
let sliceDailyData = dailyData.daily ? dailyData.daily.slice(1,7) : null
//using map with new array
{sliceDailyData ? sliceDailyData.map((i) => <div key={i.dt} className="col-lg-3 box-daily-weather">
{getCurrentDate(new Date())}
<NavLink to={'/WeatherDayStat'}>
<div className="temp-day">{Math.ceil(i.temp.day)}°C</div>
<div className="feels-like">Feels like: {Math.ceil(i.feels_like.day)} °C</div>
<div className="daily-weather-condition">Conditions: {i.weather.map(i => i.main)} </div>
</NavLink>
</div>
): null}
Speaking about ID, I have created variable which generte id and push it to array.
You can store your dailyData.daily array in an object and then retrieve it with key, you can use something simple but SEO friendly like obj['day'+(dailyData.daily.indexOf(d)+1)]
obj{}
dailyData.daily.forEach(d=>array.indexOf(d)<=5?obj[String(Math.random())]=d:null)

React - Split on string not having any effect

I am trying to split a given text at each \n in order to put them on individual lines.
The problem is, in React, I am using the following code:
const details = property.details !== undefined
? property.details.split("\n").map((item, i) => {
return <p key={i}>{item}</p>;
})
: "";
but there is no splitting made whatsoever. The returned array is simply the whole string. I tried the same string in the console of the browser and it works there.
Also, the typeof property.details is string.
What am I missing?
My render function for this component is:
render() {
const property = this.state.property;
const details =
property.details !== undefined
? property.details.split(/\r?\n/).map((item, i) => {
return <p key={i}>{item}</p>;
})
: "";
return (
<Fragment>
{this.state.isLoading ? (
<div className="sweet-loading" style={{ marginTop: "120px" }}>
<BarLoader
sizeUnit={"px"}
css={override}
size={200}
color={"#123abc"}
loading={this.state.isLoading}
/>
</div>
) : (
<div className="container p-4" style={{ marginTop: "4rem" }}>
<div className="row align-items-center">
<div className="row display-inline p-3">
<h3>{property.title}</h3>
<h5 className="fontw-300">{property.zone}</h5>
<h1 className="mt-5 price-font-presentation">
{property.sale_type} -{" "}
<strong>{property.price.toLocaleString()} EUR</strong>
</h1>
</div>
<Carousel>
{property.images.map(image => (
<img key={image.id} src={image.image} />
))}
</Carousel>
<div className="row p-3">
<h3 className="border-bottom">Detalii</h3>
{details}
</div>
</div>
</div>
)}
</Fragment>
);
}
Maybe I should mention that the information is taken with a django_rest api. Maybe there is a problem with returning \n in a string from there.
It might happen because client OS uses different symbols for new lines. Try this, it's multi-platform:
const details = property.details !== undefined
? property.details.split(/\r?\n/)
: [];
EDIT:
typeof property.details is string because it's string. calling split on property.details returns array, but string remains to be string.
From your updated code sample I can see that you are basically rendering details, which results in array transforming to string back again but without line seperators.
Maybe you have to map it to paragraphs for example:
<h3 className="border-bottom">Detalii</h3>
{details.map(detail => <p>{detail}</p>)}
Also, try white-space: pre; css property as alternative
Have you tried this version:
const details = property.details !== undefined
? property.details.split('\r\n').map((item, i) =>
<p key={i}>{item}</p>;
)
: '';
(It's the extra \r)

How to access index of nested items in JS object?

This is where I should probably add something to fix the issue, I am stuck with this index that returns the whole object, my goal is to print the index of clicked item, please someone help
const listItems = this.state.list.map((item, index) =>
Object.values(item).map(nestedItem => (
<div>
<Card.Header>
{nestedItem.title}
</Card.Header>
<div class="ui buttons fluid">
<button
onClick={() => this.upvote(index)}
>
UPVOTE
</button>
</div>
))
);
The code below is working correctly it's just that I hard coded the index I am looking for 2 in this example
console.log(Object.keys(this.state.list[index])[2]);
And this is the whole object, all I need now is the index of it
0: "-LORYsI9mLP8mu_2BTKS"
1: "-LORZVOq8SMUgTOPgpXK"
2: "-LORZtqZeg3nyOW4p9I1"
3: "-LOYbElg81jbPtao2nl4"
4: "-LOZ3pNNMAOtNxMWNDi4"
Do you just need the index of the inner mapping? I'm confused by the question still, but perhaps something like this.
const listItems = this.state.list.map((item, index) =>
Object.values(item).map((nestedItem, nestedIndex) => (
<div>
<Card.Header>
{nestedItem.title}
</Card.Header>
<div class="ui buttons fluid">
<button
onClick={() => this.upvote(index, nestedIndex)}
>
UPVOTE
</button>
</div>
))
);
If this doesn't work, could you post an example of your data structure?
here are the docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#Syntax
If you want the indices for the nested items, the you'll need to have another variable in the map fn
Object.values(item).map((nestedItem, nestedIndex) => <div>...</div>
and then use the nestedIndex variable in your upvote method

React component with two sets of children

I'm creating a component that needs to take in two sets of children and to be placed in two different parts of a component.
let CreditCardForm = ({icons, fields}) => (
<div>
<div className='some'>
<div className='special'>
<div className='nesting'>
{icons}
</div>
</div>
</div>
{fields}
</div>
)
let CreditCardFormUsage = () => {
let icons = () => (
<Icons>
<IconBogus/>
<IconVisa/>
<IconPaypal/>
<IconMore/>
</Icons>
)
let fields = () => (
<CreditCardFields>
<FieldCardNumber/>
<FieldName/>
<FieldExpirey/>
<FieldCCV/>
</CreditCardFields>
)
return (
<CreditCardForm icons={icons} fields={fields}/>
)
}
The code above should work, my question is it possible to grab those property values based on the children in the element itself, and have something more natural?
<CreditCardForm>
<Icons>
<IconBogus/>
<IconVisa/>
<IconPaypal/>
<IconMore/>
</Icons>
<CreditCardFields>
<FieldCardNumber/>
<FieldName/>
<FieldExpirey/>
<FieldCCV/>
</CreditCardFields>
</CreditCardForm>
Yes, this.props.children will return an array so if you always want to load specific children, then just reference those children by index in your wrapper. Then you could just turn icons and fields into wrapper components. Here is a working jsfiddle. See how the render method of App is exactly what you want.
CreditCardForm render:
<div>
<div className='some'>
<div className='special'>
<div className='nesting'>
{this.props.children[0]}
</div>
</div>
</div>
{this.props.children[1]}
</div>
Fields and Icons render:
<div>{this.props.children}</div>
App render:
<CreditCardForm>
<Icons>
<IconBogus />
</Icons>
<Fields>
<FieldCardNumber />
<FieldName />
</Fields>
</CreditCardForm>
yes, you can do it with child props. Read more #docs:
https://facebook.github.io/react/tips/children-props-type.html
And of course check out React.Children
https://facebook.github.io/react/docs/top-level-api.html#react.children

Categories

Resources