How can I make a difference on only one card when I make a card list by map function with React? - javascript

I want to make cards list like the 'Goal' picture. If you look at first picture, last card has only difference at price and last part. But I don't know how to make difference when I use array and map function. Should I resolve it by using conditional rendering? If I could, please let me know how to make it. I'd appreciate it if you let me know thank you.
Goal
Mine
this is Section5Bottom.jsx file. There are array and map function in this file. I erased 'people' element in last array but css still remains so it still shows little pink background-color like 'Mine' picture above
import React from 'react';
import "../section5.css";
import Section5Card from './Section5Card';
const array = [
{
'id' : '1',
'img' : "https://cdn.inflearn.com/public/courses/324119/course_cover/07c45106-3cfa-4dd6-93ed-a6449591831c/%E1%84%80%E1%85%B3%E1%84%85%E1%85%AE%E1%86%B8%205%20%E1%84%87%E1%85%A9%E1%86%A8%E1%84%89%E1%85%A1%204.png",
'title' : '실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발',
'name' : '김영한',
'star' : '5',
'comment' : '(1473)',
'price' : '₩88,000',
'people' : '+12400명',
},
{
'id' : '1',
'img' : "https://cdn.inflearn.com/public/courses/324671/course_cover/638eee1a-6381-402d-a17b-3724751414f1/frontend-env-eng.png",
'title' : '프론트엔드 개발환경의 이해와 실습 (webpack, babel, eslint..)',
'name' : '김정환',
'star' : '5',
'comment' : '(169)',
'price' : '₩69,300',
'people' : '+2000명',
},
{
'id' : '1',
'img' : "https://cdn.inflearn.com/public/courses/329477/cover/80fb90fb-0212-4eec-8fb0-7875622b198e/329477-eng.png",
'title' : '애플 웹사이트 인터랙션 클론!',
'name' : '1분코딩',
'star' : '5',
'comment' : '(187)',
'price' : '₩77,000',
'people' : '+3200명',
},
{
'id' : '1',
'img' : "https://cdn.inflearn.com/public/courses/327193/cover/010c8faf-bfc5-4c6a-9623-a68dc3b38697/327193-eng.png",
'title' : 'Slack 클론 코딩[백엔드 with NestJS + TypeORM]',
'name' : '조현영',
'star' : '4.5',
'comment' : '(58)',
'price' : '₩44,000',
'people' : '+1100명',
},
{
'id' : '5',
'img' : "https://cdn.inflearn.com/public/courses/329477/cover/80fb90fb-0212-4eec-8fb0-7875622b198e/329477-eng.png",
'title' : '모바일 웹 퍼블리싱 포트폴리오 with Figma',
'name' : '코딩웍스(Coding Works)',
'star' : '0',
'comment' : '(0)',
'discount' : '₩132,000',
'price' : '₩92,400',
'new' : '새강의',
'sale' : '할인중'
}
]
function Section5Bottom() {
return (
<div id='Section5Bottom'>
{
array.map((a, i) => {
return (
<Section5Card products={a} key={i} />
)
})
}
</div>
)
}
export default Section5Bottom;
and this is Section5Card.jsx file. This file is imported to Section5Bottom.jsx file
import React from 'react';
import '../section5.css';
function Section5Card(props) {
return (
<div id='Section5Card'>
<img src={props.products.img} id="Section5CardImg" />
<div id='Section5CardTitle'>
{props.products.title}
</div>
<div id='Section5CardName'>
{props.products.name}
</div>
<div id='Section5CardComment'>
{props.products.comment}
</div>
<div id='Section5CardPrice'>
{props.products.price}
</div>
<div id='Section5CardPeople'>
{props.products.people}
</div>
{
props.products.new && (
<div id='Section5CardNew'>
{props.products.new}
</div>
)
}
{
props.products.sale && (
<div id='Section5CardSale'>
{props.products.sale}
</div>
)
}
</div>
)
}
export default Section5Card;

function:
const ConditionalWrapper = ({ children, condition }) => {
return condition ? (
<div>{discount price}</div>
) : (
<>{price}</>
)
}
how to use above function
<ConditionalWrapper condition={condition}>children</ConditionalWrapper>
According to the above problem. you should apply condition if discount price avaliable. if true change css for price. I mean text to gray & strike decoration. Add discount price afterward. if false return price with no additional css. like it is in the picture. I'm not fluent in English. I hope you understood. Still facing problem comment

Related

How to set values for react-select with dynamic rendering of select fields?

I was working on rendering dynamic select fields and trying to set the values for them . Let me explain clearly what i am trying .
At first i have this useState :
const add_actions_options = [
{value : "val1" , label:VAL1},
{value : "val2" , label:VAL2 })}
]
const [ actions , setActions ] = useState<any | undefined>([{type : add_actions_options[0].value , label : "" , data : ""} ])
Initially 1 select field is rendered , but later i have a button which renders more select fields ( Max 4 select fields ) below is the related code for dynamic rendering of Select fields fields:
function addAction(){
if(actions.length < 4 ){
setActions([...actions , {type : add_actions_options[0].value , label : "" , data : ""}])
} else {
toast('max 4 allowed', { type: "error" })
}
}
const handleTypeChange = (index, value) => {
const updatedFields = [...actions];
updatedFields[index].type = value;
setActions(updatedFields);
}
const handleLabelChange = (index , value) => {
const updatedFields = [...actions];
updatedFields[index].label = value;
setActions(updatedFields);
}
const handleDataChange = (index, value) => {
const updatedFields = [...actions];
updatedFields[index].data = value;
setActions(updatedFields);
}
<button className='btn btn-primary btn-sm btn-block' onClick={() => addAction()}>{intl.formatMessage({id: 'ADD.ACTION'})}</button>
<div className='row my-6 '>
{actions.map((item , index) => {
return(
<div key={index} className='row my-6'>
<div className='col-4'>
<h4><label className="form-label">{intl.formatMessage({ id: 'TEMPLATE.TYPE' })}*</label></h4>
<Select
defaultValue={add_actions_options[0]}
onChange={(value) => handleTypeChange(index, value)}
options={add_actions_options}
/>
</div>
<div className='col-4'>
<h4><label className="form-label">{intl.formatMessage({ id: 'TEMPLATE.LABEL' })}*</label></h4>
<input
className="form-control form-control-lg form-control-solid"
name='action.label'
type="text"
maxLength={30}
onChange={(event) => handleLabelChange(index, event.target.value)}
value={actions.label}
required
/>
</div>
<div className='col-4'>
<h4><label className="form-label">{intl.formatMessage({ id: 'TEMPLATE.DATA' })}*</label></h4>
<input
className="form-control form-control-lg form-control-solid"
name='action.data'
type="text"
maxLength={100}
onChange={(event) => handleDataChange(index, event.target.value)}
value={actions.data}
required
/>
</div>
</div>
)
})}
</div>
Here's what i wanted to achieve , the data and label values for actions are working as expected , but the value is being set differently when i choose options from select dropdown,
Initially if i do not change any values for select component it is working as expected , the output :
0: {type:'val1' , label : 'qwe' , data:'ujm'}
1: {type:'val1' , label : 'ujhn' , data: 'uhn'}
Note : In above scenario I am just rendering one more select fields and initially the type is set to add_actions_options[0].value , but if I try to select val2 from the dropdown the output turns to :
0: {type: {value: 'val2', label: 'VAL2'} , label : 'qwe' , data:'ujm'}
1: {type:'val1' , label : 'ujhn' , data: 'uhn'}
//DESIRED OUTPUT WHEN I CHOOSE DIFFERENT OPTION IN SELECT TAG
0: {type: 'val2' , label : 'qwe' , data:'ujm'}
1: {type:'val1' , label : 'ujhn' , data: 'uhn'}
I just want the val2 to be placed within type , but its placing whole selected option , can anyone help me pass this ? Would be great help for me !
Regards

"react" Each child in a list should have a unique "key" prop

An error seems to occur because the key value is not entered in the map function, but I do not know how to modify the code.
The array is structured like this:
const tabContArr=[
{
tabTitle:(
<span className={activeIndex===0 ? "is-active" : ""} onClick={()=>tabClickHandler(0)}>0</span>
),
},
{
tabTitle:(
<span className={activeIndex===1 ? "is-active" : ""} onClick={()=>tabClickHandler(1)}>1</span>
),
},
{
tabTitle:(
<span className={activeIndex===2 ? "is-active" : ""} onClick={()=>tabClickHandler(2)}>2</span>
),
},
{
tabTitle:(
<span className={activeIndex===3 ? "is-active" : ""} onClick={()=>tabClickHandler(3)}>3</span>
),
}
];
An error occurs in the map function part.
{tabContArr.map((section)=>{
return section.tabTitle
})}
Try with React Fragments with a key attribute as mentioned in React docs
{tabContArr.map((section, index)=>{
return <React.Fragment key={`section-tab-${index}`}>{section.tabTitle}</React.Fragment>
})}
What you have done is not the right way.
If you have data, instead of passing the ReactElement into the array you should pass it into the map function like this:
{tabContArr.map((tab, index)=>{
return <span
className={activeIndex === index ? "is-active" : ""}
onClick={()=>tabClickHandler(index)}
key={`tab-${index}`}>index</span>
})}

Can I change the order of li elements conditionally in React

I have a list with side menu options, some of them are normal categories, some of them are static, here is my code:
//const type = the type is either categories or static
{items.map((item, index) => {
return (
<li>
{type !== 'categories' ? (
<> Static </>
) : (
<> Category </>
)}
</li>
);
})}
So in the items that are of type static I have a variable that comes from the back end which is called display, its value could be either 'top' or 'bottom', what I want to do is, if the type is equal to 'static', to move the order of the li to the value item.display has, for example:
<li>----</li> //type static //item.display: top
<li>++++</li> //type static //item.display: bottom
<li>""""</li> //type categories //item.display doesn't exist here
<li>::::</li> //type categories //item.display doesn't exist here
In the example above the li filled with the symbol '+' has a value of display: bottom,
so it should go to the bottom of the list and the list should now look like that:
<li>----</li> //type static //item.display: top
<li>""""</li> //type categories //item.display doesn't exist here
<li>::::</li> //type categories //item.display doesn't exist here
<li>++++</li> //type static //item.display: bottom
How can I implement that logic, I already tried doing it with inline styles like so:
<li style={{order: item.display === "top" ? items.length.toString() : "1"}}>
I am not even sure if this is a viable HTML, but it didn't work anyway, what are your suggestions for solving this problem?
You can control the order using items.sort() before you map it into virtual dom nodes.
Example:
import React from 'react'
function App() {
let items = [
{ text: '----', type: 'static', display: 'top' },
{ text: '++++', type: 'static', display: 'bottom' },
{ text: '""""', type: 'categories' },
{ text: '::::', type: 'categories' },
]
return (
<ul>
{items
.sort((a, b) =>
a.display === b.display
? 0
: a.display === 'top'
? -1
: a.display === 'bottom'
? 1
: 0,
)
.map(item => (
<li>
{item.type !== 'categories' ? (
<> Static </>
) : (
<> Category </>
)}
</li>
))}
</ul>
)
}
export default App
output:
<ul>
<li>----</li>
<li>""""</li>
<li>::::</li>
<li>++++</li>
</ul>
A possible solution would be that you have a mapping from string to number and a little helper function
const displayMap = {
top: 0,
default: 1,
bottom: 2
};
const compareDisplay(a,b){
let disp1 = a.display ?? 'default';
let disp2 = b.display ?? 'default';
return displayMap[disp1] > displayMap[disp2] ? 1 : -1;
}
in case if display is not defined, it will take default, which is mapped to the middle value
In case display also contains other values than top or bottom, you need to add a bit of logic to the helper function
you can use this mapping, to sort your objects before mapping
{
items.sort((a,b) => compareDisplay(a,b)).map((item, index) => {
return (
<li>
{
type !== 'categories' ?
(<> Static </>) :
(<> Category </>)
}
</li>
);
})}

React.js: table inside expandable rows

I have an antd expandable rows table with vehicles. I want to display in the expanded row all the bookings of the specific vehicle.
This is the vehicles table with its datasource:
const vehiclesData = (
props.vehicles.map(vehicle =>{
addMonths(2,vehicle.insuranceStart);
return {
"key":vehicle.id,
"brand":vehicle.brand !=null ? vehicle.brand.brand : '',
"model":vehicle.model !=null ? vehicle.model.model : '',
"color":vehicle.color,
"plate":vehicle.plate,
"insuranceStart":moment(vehicle.insuranceStart).format('DD/MM/YYYY'),
"insuranceType":vehicle.insuranceType !=null ? vehicle.insuranceType.company + ' ('+vehicle.insuranceType.duration +' Μήνες'+')' : '',
"brand_id":vehicle.brand !=null ? vehicle.brand.id : '',
"model_id":vehicle.model !=null ? vehicle.model.id : '',
"insurance_id":vehicle.insuranceType !=null ? vehicle.insuranceType.id : '',
"bookings": vehicle.bookings != null ? vehicle.bookings : ''
}
})
)
<Table
expandedRowRender={record => <p style={{ margin: 0 }}>{record.bookings}</p>}
dataSource={vehiclesData}
size="small"
bordered
columns = {columns}
loading={props.vehiclesLoading!=null ? props.vehiclesLoading : false}
/>
This is what I have tried so far which obviously doesn't work. I was thinking that I can replace <p style={{ margin: 0 }}>{record.bookings}</p> with another table but I don't know how I can configure the child's table datasource in order to fetch only the bookings of the expanded row's vehicle.
You can pass another <Table dataSource={record.bookings} /> like you suggested to the expandableRowRender with new column names.
Here's a sample code box that I edited from the example on the AntD documentation.
https://codesandbox.io/s/compassionate-microservice-4z5gq
Hope that helps

React : Warning: Failed prop type: Cannot read property 'apply' of undefined

I am working on a navbar in react and i have been getting this error, and i have not found a working solution yet. Here is the piece of my code. I am trying to use react prop-types library to validate my navbar props with either a link or a dropdown. Below is the piece of code i have written.
NavBar.js
const NavBar = ({ navItems }) => {
return (
<nav role="navigation" className={styles.navBar}>
<Logo type="text">
ABCD
</Logo>
<div className={[styles.collapse, styles.noCollapse].join(' ')}>
<SearchBox />
<NavItems navItemsData={navItems} />
</div>
</nav>
);
};
NavItems.js
const NavItems = ({ navItemsData }) => {
return (
<ul className={styles.navItems}>
{navItemsData.map(navItemData => {
let navItem = <NavItem {...navItemData} key={navItemData.id} />
if (navItemData.type === 'dropdown') {
navItem = <NavDropDownItem {...navItemData} key={navItemData.id} />
}
return navItem;
})}
</ul>
);
};
PropTypes Checker(in same file as NavItems) :-
NavItems.propTypes = {
navItemsData: PropTypes.arrayOf(PropTypes.shape({
id : PropTypes.number,
type: PropTypes.oneOf(['link', 'dropdown']).isRequired,
linkText: requiredIf(PropTypes.string.isRequired, props => props.type === 'link'),
title : requiredIf(PropTypes.string.isRequired, props => props.type === 'dropdown'),
dropDownList: requiredIf(PropTypes.arrayOf(PropTypes.shape({ linkText: PropTypes.string.isRequired })), props => props.type === 'dropdown')
}))
};
I keep getting this warning in the console. As follows :-
Warning: Failed prop type: Cannot read property 'apply' of undefined
in NavItems (at NavBar.js:15)
in NavBar (at App.js:35)
in div (at App.js:34)
in App (at src/index.js:7)
The props i am passing :
const navItems = [{
id : 1,
type : 'link',
linkText : 'Link1'
},{
id : 2,
type : 'link',
linkText : 'Link2'
}, {
id : 3,
type : 'link',
linkText : 'Link3'
}, {
id : 4,
type : 'link',
linkText : 'Link4'
},{
id : 5,
type : 'link',
linkText : 'Link5'
},{
id : 6,
type : 'dropdown',
dropDownList : [{
linkText : 'LinkText'
}]
}]
Any help would be appreciated.
When you are using requiredIf, the first parameter should be the expected type. But it should not have the isRequired part. For example, your validation should be as follows.
NavItems.propTypes = {
navItemsData: PropTypes.arrayOf(PropTypes.shape({
id : PropTypes.number,
type: PropTypes.oneOf(['link', 'dropdown']).isRequired,
linkText: requiredIf(PropTypes.string, props => props.type === 'link'),
title : requiredIf(PropTypes.string, props => props.type === 'dropdown'),
dropDownList: requiredIf(PropTypes.arrayOf(PropTypes.shape({ linkText: PropTypes.string.isRequired })), props => props.type === 'dropdown')
}))};
There is no need to do PropTypes.string.isRequired inside requiredIf as it already covers the required case.
NavItems.propTypes = {
navItemsData: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number,
type: PropTypes.oneOf(["link", "dropdown"]).isRequired,
linkText: requiredIf(PropTypes.string, props => props.type === "link"),
title: requiredIf(PropTypes.string, props => props.type === "dropdown"),
dropDownList: requiredIf(
PropTypes.arrayOf(PropTypes.shape({ linkText: PropTypes.string })),
props => props.type === "dropdown"
)
})
)
};

Categories

Resources