React map to render with row - javascript

Hi I have an array with 6 element and itterate 3 in each row with map function by dividing the array into three elements and I dont do this without dividing array my code is below
<div
style={{
width: "100%",
display: "flex",
flexWrap: "wrap",
justifyContent: "space-around",
}}
>
{array1.map((item, index) => {
return (
<div className="col-4">
<img src={item.icon} />
<p >{item.title}</p>
<p >
{item.description}
</p>
</div>
);
})}
</div>
<div
style={{
width: "100%",
display: "flex",
flexWrap: "wrap",
justifyContent: "space-around",
}}
>
{array2.map((item) => {
return (
<div className="col-4 item-item">
<img src={item.icon} />
<p>{item.title}</p>
<p>
{item.description}
</p>
</div>
);
})}
</div>
I want to map 3 elements per row and without dividing the 6 elements array to 3 elements thanks for suggestion

You can just use map for this purpose
array.map((element, index) => {
if (index % 3 === 0)
return (
<div className="row">
<div className="col">{array[index]}</div>
<div className="col">{array[index + 1]}</div>
<div className="col">{array[index + 2]}</div>
</div>
);
})
If you decide to change number of elements per row, you can just tweak the values.
Check a working example here

I'd recommend you use this function here to split the array up into chunks. From there you can perform a nested map!
// # Source https://github.com/30-seconds/30-seconds-of-code/blob/master/snippets/chunk.md
const chunk = (arr, size) =>
Array.from({ length: Math.ceil(arr.length / size) }, (v, i) =>
arr.slice(i * size, i * size + size)
);
console.log(chunk([1, 2, 3, 4, 5,6], 3));
// Output - [[1,2,3],[4,5,6]]

Considering you already know the length of array which is 6. Assuming array name to be "originalArray"
<div
style={{
width: "100%",
display: "flex",
flexWrap: "wrap",
justifyContent: "space-around",
}}
>
{originalArray.map((item, index) => {
(index<3)?
return (
<div className="col-4">
<img src={item.icon} />
<p >{item.title}</p>
<p >
{item.description}
</p>
</div>
}
: return( <div className="col-4 item-item">
<img src={item.icon} />
<p>{item.title}</p>
<p>
{item.description}
</p>
</div>)
);
})}
</div>

I am not sure about performance of this snippet, but you can achieve what you want with reduce instead of map
array.reduce<JSX.Element[]>((rows, item, itemIndex) => {
const ComponentToRender = <Component />
if (itemIndex % 3 === 0) {
rows.push(<Row key={rows.length + 1}>{ComponentToRender}</Row>);
} else {
const LastRow = rows[rows.length - 1];
const cols = [
...React.Children.toArray(LastRow.props.children),
ComponentToRender,
];
rows[rows.length - 1] = <Row key={rows.length + 1}>{cols}</Row>;
}
return rows;
}, [])
Or just use a CSS grid system to be able to render 3 elements per row. That way you can stick to the simple map.

Related

React Recharts tooltip not showing proper data

I have two lines plotted on a recharts one is plotting everyday and the other is plotting once every quarter. The recharts tooltip only shows up for the pink line when it is directly hovered over the plotted point and says 0 when it is not over the directly plotted point. I want the tooltip to always show the data during that time. I tried adding in the "filterNull={false}" into the tooltip but it did not work. Any Ideas?
<Tooltip content={renderTooltipContentGeneric} filterNull={false} />
export const renderTooltipContentGeneric = o => {
const { payload, label } = o
const total = payload?.reduce((result, entry) => result + entry.value, 0)
return (
<div className="customized-tooltip-content-core">
<div className="list">
{payload?.map((entry, index) => (
<div
className="listItem"
key={`item-${index}`}
style={{
color: entry.color,
display: 'flex',
flexDirection: 'row',
gap: '4px',
}}
>
<div style={{ color: 'var(--white)' }}>{`${moment(label).format(
'lll'
)}: `}</div>
{`${numeral(entry.value).format('0.000 a')} `}
</div>
))}
</div>
</div>
)
}

how to put Custom Next and Prev Scroll Buttons for div react

I have two buttons. Prev and Next Button. On below, I have one horizontal div with multiple items with scroll bar.
Here's the UI looks like:
The Scroll Bar working good. But I have prev and next buttons. When I click on two buttons, it should scroll left and right. I tried. But I don't know how to make it in react.
Here's the Code I tried:
export default function App() {
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
return (
<div>
<div
style={{
display: "flex",
justifyContent: "flex-end",
gap: "50px",
marginBottom: "30px"
}}
>
<div style={{ cursor: "pointer" }}>Prev</div>
<div style={{ cursor: "pointer" }}>Next</div>
</div>
<div
style={{
display: "flex",
gap: "150px",
overflow: "scroll",
backgroundColor: "aqua"
}}
>
{data.map((item, index) => (
<div>item {item}</div>
))}
</div>
<div></div>
</div>
);
}
I don't know how to make it. Please help me with some solutions.
You can use scrollLeft property and useRef. Here is my solution.
export default function App() {
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
const ref = React.useRef(null)
const handleScroll = (offset) => {
if (ref.current) {
ref.current.scrollLeft += offset;
}
}
return (
<div>
<div
style={{
display: "flex",
justifyContent: "flex-end",
gap: "50px",
marginBottom: "30px"
}}
>
<div onClick={() => handleScroll(-30)} style={{ cursor: "pointer" }}>Prev</div>
<div onClick={() => handleScroll(30)} style={{ cursor: "pointer" }}>Next</div>
</div>
<div
style={{
display: "flex",
gap: "150px",
overflow: "scroll",
backgroundColor: "aqua"
}}
ref={ref}
>
{data.map((item, index) => (
<div key={index}>item {item}</div>
))}
</div>
<div></div>
</div>
);
}

I am trying to align movieCards side by side. But they are aligning in a column

This is my movie list component. I tried different attributes in the div section to get the cards in a row. But all the cards are aligned in a column.should i use either flex or grid.If yes how can i use them. Even i referred to many resources for this.But didn't worked for me. What should I do to get the cards as I wish . Please assist me.
import React, { useState } from 'react'
import { useSelector } from 'react-redux'
import MovieCard from './MovieCard'
const MoviesList = (props) => {
const [search, setSearch] = useState('')
const [filterBy, setFilterBy] = useState([])
const [orderBy, setOrderBy] = useState('')
const movies = useSelector((state) => {
console.log(state.movies)
return state.movies
})
const handleChange = (e) => {
const inputValue = e.target.value
setSearch(inputValue)
const filteredValue = movies.filter((movie) => {
return movie.Title.toLowerCase().includes(inputValue)
})
setFilterBy(filteredValue)
console.log(filteredValue)
}
const handleSelectChange = (e) => {
setOrderBy(e.target.value)
}
const show = (movies) => {
switch (orderBy) {
case 'a-z': return [...movies.sort((a, b) => a.Title.localeCompare(b.Title))]
case 'z-a': return [...movies.sort((a, b) => b.Title.localeCompare(a.Title))]
case '1-100': return [...movies.sort((a, b) => a.imdbRating - b.imdbRating)]
case '100-1': return [...movies.sort((a, b) => b.imdbRating - a.imdbRating)]
default: return [...movies]
}
}
return (
<div className='container'>
<div className='d-flex mb-3 '>
<h1 style={{ textAlign: 'center', border: 'solid lightgreen', backgroundColor: 'white' }} >My Movie List</h1>
<form style={{ float: 'right', marginRight: '35px' }}>
<input type="text" placeholder='search by name' value={search} onChange={handleChange} />
<select value={orderBy} onChange={handleSelectChange} >
<option value="">orderBy</option>
<option value="a-z">a-z</option>
<option value="z-a">z-a</option>
<option value="1-100">1-100</option>
<option value="100-1">100-1</option>
</select>
</form>
</div>
<div className=" row pt-2 justify-content-around " style={{ textAlign: 'center', width: '100%' }}>
{
filterBy.length > 0 ? (
filterBy.map((movie) => {
return <MovieCard key={movie.imdbID} Poster={movie.Poster} Title={movie.Title} imdbRating={movie.imdbRating} imdbID={movie.imdbID} />
})
) : (
show(movies).map((movie) => {
return <MovieCard key={movie.imdbID} {...movie} />
})
)
}
</div>
</div >
)
}
export default MoviesList
And this is Movie card component
import React from 'react'
import { useDispatch } from 'react-redux'
import { removeMovie } from '../actions/moviesAction'
const MovieCard = (props) => {
console.log(props)
const { Title, Poster, imdbRating, imdbID } = props
const dispatch = useDispatch()
return (
<div className="card mt-2 p-2 bd-highlight border shadow rounded" style={{ width: '16rem' }}>
<img className="card-img-left pt-2" src={Poster} alt={Title} style={{ width: '200px', height: '200px' }} />
<div className="card-body">
<h4 className="card-title">Name : {Title}</h4>
<h5 className="card-title">Ranking : #{imdbRating}</h5>
<ion-icon name="trash" style={{ color: 'red' }} onClick={() => {
dispatch(removeMovie(imdbID))
}}>
</ion-icon>
</div>
</div>
)}
export default MovieCard
Please help me to resolve this. Thankyou
Try providing display: flex, to the div rendering MovieCard, like this:
<div className=" row pt-2 justify-content-around "
style={{ textAlign: 'center', width: '100%', display: 'flex' }}>
{
filterBy.length > 0 ? (
filterBy.map((movie) => {
return <MovieCard key={movie.imdbID} Poster={movie.Poster} Title={movie.Title} imdbRating={movie.imdbRating} imdbID={movie.imdbID} />
})
) : (
show(movies).map((movie) => {
return <MovieCard key={movie.imdbID} {...movie} />
})
)
}
</div>
You can just use flex here.
add the below code to your parent div (div containing all the cards).
If you have a css file add a class to that div and add the following styles.
display: flex;
flex-wrap: wrap;
justify-content: center;
or if you want to use inline styling do the following:
<div className=" row pt-2 justify-content-around " style={{ textAlign: 'center', width: '100%', display: "flex", flexWrap: "wrap", justifyContent: "center" }}>
{filterBy.length > 0 ? (
filterBy.map((movie) => {
return <MovieCard key={movie.imdbID} Poster={movie.Poster} Title={movie.Title} imdbRating={movie.imdbRating} imdbID={movie.imdbID} />
})
) : (
show(movies).map((movie) => {
return <MovieCard key={movie.imdbID} {...movie} />
}
)
)}
</div>
Try using a grid method something like the code below will allow you control the number of rows and columns you want as well as size them.
**
<h1>Example { grid-template-rows: 40% 50% 50px;}</h1>
<p><strong style="color: red">The shorthand property, grid-template, can replace both </strong>grid-template-rows and grid-template-columns.</p>
<hr>
<p style="font-family: Courier">When using grid-template, the values before the slash will determine the size of each row. The values after the slash determine the size of each column. </p><hr>
<p style="font-family: Courier">In this example there are 3 rows and 3 columns added to the grid.</p><h1>Example {grid-template: 40% 50% 50px/100px 50% 200px;}</h1><br>
<p><strong style="color: red">By using the (fr) unit, we can define the size of columns and rows as a fraction of the grid’s length and width.</strong><br>Using (fr) makes it easier to prevent grid items from overflowing the boundaries of the grid.</p>
<h1>Example {grid-template: 1fr 1fr 1fr / 3fr 50% 1fr;}<br> width: 400px;<br>
**
If you're using Bootstrap then you don't need to use flex nor grid directly. This is the Bootstrap's way to achieve the desired layout:
<div className="row">
{movies.map(item => (
<div className="col">
// then here yo can put your card for the movie
</div>
))}
</div>
This is the Bootstrap doc about it

Select only a card at a time on click in reactjs

I have a react component which has some cards, When I click the plus icon in the card, it would expand and show some data for 30sec and then the data will disappear and on click it will reappear again, here is the component
import React from "react";
import { FaPlus } from "react-icons/fa";
import useTimeout from "../../../common/useTimeout";
import { Modal } from "../../../common/Modal";
import { ToggleState } from "../../../common/Toggle";
import { BsSearch } from "react-icons/bs";
import AdvancedFilter from "./AdvancedFilter";
export default function CitiesList({ cities }: { cities: any }): JSX.Element {
const [filter, setFilter] = React.useState("");
const [sortType, setSortType] = React.useState("");
const [selectedCity, setSelectedCity] = React.useState<any | null>(null);
console.log(filter);
const sorted = cities.sort((a: { name: string }, b: { name: any }) => {
const isReversed = sortType === "asc" ? 1 : -1;
return isReversed * a.name.localeCompare(b.name);
});
const onSort = (sortType: React.SetStateAction<string>) => {
console.log("changed");
setSortType(sortType);
};
const [showMeta, setShowMeta] = React.useState(false);
const handleClick = () => setShowMeta(true);
const getSelectedCity = (selectedCity: any) => {
setSelectedCity(selectedCity);
console.log("SELECTED CITY", selectedCity);
};
const [visible, setVisible] = React.useState(true);
const hide = () => setVisible(false);
useTimeout(hide, 30000);
console.log("CITIES", cities);
console.log({ selectedCity });
return (
<div style={{ marginTop: "3rem" }}>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "20px",
}}
>
<div>List of cities</div>
<div style={{ display: "flex", alignItems: "center" }}>
<div style={{ marginRight: "1rem" }}>
<ToggleState
render={({ isOpen, open, close }) => {
return (
<>
<button
type="button"
className="btn btn-primary"
onClick={() => {
isOpen ? close() : open();
}}
>
Advanced Filter
</button>
<Modal
isActive={isOpen}
modalContentWidth={"30%"}
header={() => "Advanced Filter"}
close={() => close()}
renderBody={() => {
return <AdvancedFilter close={() => close()} />;
}}
></Modal>
</>
);
}}
/>
</div>
<div style={{ position: "relative", marginRight: "1rem" }}>
<input
type="text"
placeholder="Filter"
name="namePrefix"
style={{ padding: "0.35rem" }}
onChange={(e: any) => {
setFilter(e.target.value);
}}
/>
<div style={{ position: "absolute", top: "5px", right: "5px" }}>
<BsSearch size="16" />
</div>
</div>
<div style={{ width: "8rem" }}>
<div className="btn-group">
<button
type="button"
className="btn dropdown-toggle sort-button"
data-bs-toggle="dropdown"
aria-expanded="false"
>
{sortType === "asc"
? "Ascending"
: sortType === "desc"
? "Descending"
: "Select"}
</button>
<ul className="dropdown-menu sort-button">
<li>
<button
className="dropdown-item"
type="button"
onClick={() => onSort("asc")}
>
Ascending
</button>
</li>
<li>
<button
className="dropdown-item"
type="button"
onClick={() => onSort("desc")}
>
Descending
</button>
</li>
</ul>
</div>
</div>
</div>
</div>
<div>
<div>
<div className="row">
{cities &&
sorted.map((item: any, index: number) => (
<div className="col-lg-3" key={index}>
<div
className="card"
style={{
textAlign: "center",
display: "flex",
justifyContent: "center",
paddingBottom: "1rem",
marginBottom: "1rem",
marginRight: "1rem",
}}
>
<div className="card-body">
<h5 className="card-title">{item.name}</h5>
</div>
{visible && showMeta ? (
<div>
<p>Longitude: {item.longitude}</p>
<p>Latitude: {item.latitude}</p>
<p>Population: {item.population}</p>
{/* <p>Time Zone: America</p> */}
</div>
) : (
<div
onClick={() => {
handleClick();
getSelectedCity(item.id);
}}
style={{ cursor: "pointer" }}
>
<FaPlus size="18" />
</div>
)}
</div>
</div>
))}
</div>
</div>
</div>
<div
style={{ marginTop: "30px", display: "flex", justifyContent: "center" }}
>
{cities && cities.length > 10 ? (
<button className="secondary-button">Load More</button>
) : (
<p>There are no more cities</p>
)}
</div>
</div>
);
}
here is the useTimeout function
import { useEffect, useRef } from "react";
function useTimeout(callback: () => void, delay: number | null) {
const savedCallback = useRef(callback);
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
if (delay === null) {
return;
}
const id = setTimeout(() => savedCallback.current(), delay);
return () => clearTimeout(id);
}, [delay]);
}
export default useTimeout;
Now currently if I click on one card, all the cards opens and also after when the data disappears after 30sec it does not reappear on button click. I need to reload the page to do reappear data again.I need to solve 2 issues here: 1. how can I open one card only at a time on clicking on the icon, 2. how can I reappear data on button click without refreshing the page.
As I understand you, some information is
<p>Longitude: {item.longitude}</p>
<p>Latitude: {item.latitude}</p>
<p>Population: {item.population}</p>
You have global statement of showing this info for all cards. To reduce it, make new component which will have local state of this info:
import React from 'react'
const timeout = 15000
export const CityCard = ({item, visible, getSelectedCity}) => {
const [showMeta, setShowMeta] = React.useState(false)
const handleClick = React.useCallback(()=>{
setShowMeta(true)
setTimeout(()=>{
setShowMeta(false)
},timeout )
}, [showMeta])
return(
<div className="col-lg-3" key={index}>
<div
className="card"
style={{
textAlign: "center",
display: "flex",
justifyContent: "center",
paddingBottom: "1rem",
marginBottom: "1rem",
marginRight: "1rem",
}}
>
<div className="card-body">
<h5 className="card-title">{item.name}</h5>
</div>
{visible && showMeta ? (
<div>
<p>Longitude: {item.longitude}</p>
<p>Latitude: {item.latitude}</p>
<p>Population: {item.population}</p>
{/* <p>Time Zone: America</p> */}
</div>
) : (
<button
onClick={() => {
handleClick();
getSelectedCity(item.id);
}}
type='button'
disabled={showMeta}
style={{ cursor: "pointer" }}
>
<FaPlus size="18" />
</button>
)}
</div>
</div>
)
}
At the end, add this component to the CityList

Getting the index or the value of a single object inside an array

i'm doing a MERN app
What happens is that, i'm trying to pass only one value, or index of an object in an onClick function so i can make some changes in the backend, but, it passes the whole objects not only one that i need, so, how can i get only one element from an array of objects?
This is my data, and this is the array of objects
And this is my code and how i'm trying to catch only one single value or index, in the onClick function specifically the dispatch(checkTasks(_id, todo))
<TodoContainer>
{todo.map(data => {
const { homework, _id, createdAt } = data;
return (
<HomeworkContainer key={_id}>
<HomeworkHeader>
<HomeworkHeaderName>
{user?.result?.name} <br />
<span>{moment(createdAt).fromNow()}</span>
</HomeworkHeaderName>
<span
style={{ padding: "0rem 2rem" }}
onClick={() => dispatch(deleteTasks(_id))}
>
<HomeworkButtonDelete />
</span>
</HomeworkHeader>
<AllTodoStuff white>
<SpanTodoStuff>
Date : {moment(createdAt).format("DD-MM-YY")}
</SpanTodoStuff>
</AllTodoStuff>
{homework.map((rightTodo, i) => {
const { todo, complete } = rightTodo;
return (
<>
{complete === false ? (
<AllTodoStuff key={i} Uncompleted>
<SpanTodoStuff>{todo}</SpanTodoStuff>
<p>
<span style={{ padding: "0rem 2rem" }}>
<IconDone
onClick={() =>
dispatch(checkTasks(_id, todo))
}
/>
{console.log(i)}
</span>
<span style={{ padding: "0rem 2rem" }}>
<IconCancel />
</span>
</p>
</AllTodoStuff>
) : (
<AllTodoStuff key={i} white>
<SpanTodoStuff>{todo}</SpanTodoStuff>
<p>
<span style={{ padding: "0rem 2rem" }}>
<IconDone />
</span>
<span style={{ padding: "0rem 2rem" }}>
<IconCancel
onClick={() =>
dispatch(checkTasks(_id, todo))
}
/>
{console.log(i)}
</span>
</p>
</AllTodoStuff>
)}
</>
);
})}
</HomeworkContainer>
And the output of the function it's both index, in this case
0
1
But i only need, the 0, if i catch the 0 or the 1 in the other case, but, if i grab both, both are going to change, and only have to change one, the one i click

Categories

Resources