I have a button to show details of each card in my react project like this. the amount of cards are unknown and it is based on what I get from API.
just like this, I get the data and I'm mapping it:
<div className={styles.contentCardContainer}>
{factorList !== undefined && factorList.length > 0 ? (
factorList.map((list, i) => (
<div key={uuidv4()} className={styles.contentCard}>
<IconButton
color={showListService ? "error" : "success"}
onClick={() => {
setShowlistService((prev) => !prev);
HandleFetchDetails(list.ID);
}}
sx={{
display: "flex",
alignItems: "center",
alignSelf: "flex-start",
}}
>
{showListService ? <RemoveCircle /> : <AddCircle />}
</IconButton>
<section className={styles.eachSection}>
<img src={doctorImage} alt="doctor" />
<p>{list.Doctor}</p>
</section>
<section className={styles.eachSection}>
<img src={calendarImage} alt="doctor" />
<p>
{list.Date}
{list.Time}
</p>
</section>
<section className={styles.eachSection}>
<img src={RialImage} alt="doctor" />
<p>{list.Payable} ریال</p>
</section>
<section className={styles.eachSection}>
<img src={discountImage} alt="doctor" />
<p>{list.discount}</p>
</section>
{showListService && (
<DetailsListService listDetails={listDetails} styles={styles} />
)}
</div>
))
) : (
<Alert
variant="filled"
severity="warning"
sx={{ justifyContent: "space-between" }}
>
<p>There's no data</p>
</Alert>
)}
</div>
</div>
as you can see, I have IconButton element that I mapped it so each element have this button. but there's a problem.
I just have one state.
just like this :
const [showListService, setShowlistService] = React.useState(false);
since the amount cards I will receive is unknown. I can't define multiple states, because I don't know how many I should define. this is a bad practice too.
so whenever I click on IconButton, state changes for ALL buttons. how can I make it work individually.
change state for just a button in that specific element
Create a component for the cards and each card can keep the state:
factorList.map((factor, index) => {
return <Card key={index} factor={factor} />;
});
const Card = ({ factor }) => {
const [state, setState] = useState(false);
return <div>Your card stuff here</div>;
};
Related
I have a react component where I map through a list of clients and display each client in a card.
return (
<div className='VolunteerClientsTab'>
{volunteerClients && volunteerClients.map((client) => (
<React.Fragment key={client.id}>
<div className='VolunteerClientsTab__card'>
<Avatar style={{ alignSelf: 'center', marginTop: '.5rem' }}>{client.first_name[0]}{client.last_name[0]}</Avatar>
<h2>{client.first_name} {client.last_name}</h2>
<h4>Details</h4>
<p><AiOutlineMail style={iconStyles} /> {client.email}</p>
<p><AiOutlinePhone style={iconStyles} /> {formatPhoneNumber(client.contact_number)}</p>
<h4 style={{ marginTop: '1rem' }}>Actions</h4>
<p onClick={handleOpenClientNeedsModal} className='hover-underline'><BiDonateHeart style={iconStyles} />View Needs</p>
<p className='hover-underline'><HiOutlineDocumentReport style={iconStyles} />Write Report</p>
<p className='hover-underline'><FiVideo style={iconStyles} />Contact Client</p>
</div>
<ClientNeeds open={openClientNeedsModal} handleClose={handleCloseClientNeedsModal} client={client} />
</React.Fragment>
))}
</div>
)
};
ClientNeeds is a component that renders an MUI modal to display additional client information. I am passing it the client object within the loop but when I open the modal only the client of the last index in the volunteerClients array was passed to all the modal components. Does anyone have any idea why this is happening?
ClientNeeds component
import React from 'react';
import Box from '#mui/material/Box';
import Modal from '#mui/material/Modal';
import PropTypes from 'prop-types';
const style = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 400,
bgcolor: 'background.paper',
border: '2px solid #000',
boxShadow: 24,
p: 4,
};
const ClientNeeds = ({ open, handleClose, client }) => {
return (
<div>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={style}>
<h2>{client.email}</h2>
</Box>
</Modal>
</div>
)
};
ClientNeeds.propTypes = {
open: PropTypes.bool,
handleClose: PropTypes.func,
client: PropTypes.object
};
export default ClientNeeds;
MY SOLUTION
Passing the client onClick to an additional global state object and passing that state object to the modal,
const [selectedClient, setSelectedClient] = useState(null)
also only rendering the clientNeedsModal is there is an object in that state.
<div className='VolunteerClientsTab'>
{volunteerClients && volunteerClients.map((client) => (
<React.Fragment key={client.id}>
<div className='VolunteerClientsTab__card'>
<Avatar style={{ alignSelf: 'center', marginTop: '.5rem' }}>{client.first_name[0]}{client.last_name[0]}</Avatar>
<h2>{client.first_name} {client.last_name}</h2>
<h4>Details</h4>
<p><AiOutlineMail style={iconStyles} /> {client.email}</p>
<p><AiOutlinePhone style={iconStyles} /> {formatPhoneNumber(client.contact_number)}</p>
<h4 style={{ marginTop: '1rem' }}>Actions</h4>
<p onClick={() => handleOpenNeeds(client)} className='hover-underline'><BiDonateHeart style={iconStyles} />View Needs</p>
<p className='hover-underline'><HiOutlineDocumentReport style={iconStyles} />Write Report</p>
<p className='hover-underline'><FiVideo style={iconStyles} />Schedule Meeting</p>
</div>
{selectedClient ? (
<ClientNeeds open={openClientNeedsModal} handleClose={handleCloseClientNeedsModal} client={selectedClient} />
) : null}
</React.Fragment>
))}
</div>
const handleCloseClientNeedsModal = () => {
setSelectedClient(null)
setOpenClientNeedsModal(false);
}
const handleOpenNeeds = (client) => {
setSelectedClient(client)
handleOpenClientNeedsModal()
}
This allows me to pass any individual object within my array to the modal component as originally desired
I've custom multiselect dropdown which is implemented as given below:
const RequestExpertSupport = function Basic({
data,
onSelectedItemsChanged,
selectedItemsIndices,
}) {
const props = {
data: [
"My account and profile",
"Understanding my footprint",
"Target setting",
"Specific actions",
"Creating a plan",
"Finance, funding and grants ",
"Using the Carbon Planner platform",
"Other",
],
selectedItemsIndices: [],
};
//
const handleSelectedItemsChanged = useCallback(
selectedItemsIndices => {
onSelectedItemsChanged(selectedItemsIndices);
},
[onSelectedItemsChanged]
);
function onSelectedItemsChanged(selectedItemsIndices) {
console.log(selectedItemsIndices);
}
function renderItem(datum, index) {
return <span>{datum}</span>;
}
return (
<RequestExpertSupportDiv>
<div className="container">
<div className="formContainer">
<form>
<label className="helpLabel">What would you like help with?</label>
<DropdownListTrigger
dropdownList={
<MultiSelectDropdownList
id="multiSelectDrop"
data={props.data}
onSelectedItemsChanged={handleSelectedItemsChanged}
renderItem={renderItem}
selectedItemsIndices={selectedItemsIndices}
/>
}
position="right"
className="ddlFilter"
>
<button className="zb-button zb-button-secondary zb-button-with-icon-after">
<span>Choose topic(s)</span>
<Icon
name="chev-down-xsmall"
style={{
verticalAlign: "text-bottom",
}}
title={null}
/>
</button>
</DropdownListTrigger>
<div className="selectedTopics">Selected topics are:</div>
<label className="tellUsLabel">What would you like help with?</label>
<textarea
name="helpReview"
rows="4"
cols="43"
className="textArea"
style={{ width: "410px", height: "290px", marginTop: "2%" }}
placeholder="Type your message here ..."
></textarea>
<button className="sendBtn" name="sendBtn">
Send
</button>
</form>
</div>
</div>
</RequestExpertSupportDiv>
);
};
export default RequestExpertSupport;
This code fetches the indices of selected options in Multiselect dropdown.
function onSelectedItemsChanged(selectedItemsIndices) {
console.log(selectedItemsIndices);
}
Console is given below:
Now I want to display those selected options as tags like this:
This is the code for tags:
<Div
flex="flex"
display="inline-flex"
height="36px"
borderWidth="1px solid #009FAC"
borderRadius="3px"
backgroundColor="#def8fa"
justifyContent="space-around"
alignItems="center"
marginRight="10px"
marginBottom="10px"
style={{ minWidth: "92px", maxWidth: "175px" }}
>
<span
padding="0px"
fontSize="14px"
lineHeight="20px"
fontWeight="400"
marginLeft="5px"
marginRight="5px"
style={{ maxWidth: "142px" }}
>
// need to put selected options here
</span>
<img name="close-inverted-small" onClick={event => removeSelectedTopic(event, topicId)} />
</Div>
I'm not getting how to link function SelectedItemsChanged(selectedItemsIndices) with that tags frontend code to dosplay selected options as tags...
You need to put all the codes in the last part in a loop for rendering data. for example you should write last part as below by using selectedItemIncedies and data:
{ selectedItemIncedies.map ((selectedItemIndex) => { return (<Div
flex="flex"
display="inline-flex"
height="36px"
borderWidth="1px solid #009FAC"
borderRadius="3px"
backgroundColor="#def8fa"
justifyContent="space-around"
alignItems="center"
marginRight="10px"
marginBottom="10px"
style={{ minWidth: "92px", maxWidth: "175px" }}
<span
padding="0px"
fontSize="14px"
lineHeight="20px"
fontWeight="400"
marginLeft="5px"
marginRight="5px"
style={{ maxWidth: "142px" }}
>
{data.find((item, index)=> index===selectedItemIndex)}
</span>
<img name="close-inverted-small" onClick={event => removeSelectedTopic(event, topicId)} />
</Div>)})}
You'll want to render the tags using map based on the selectedItemsIndices array. First, pass the selectedItemsIndices array as props to a TagListComponent.
Once that's all set, render each of the selected tags in selectedItemsIndices in the TagListComponent
TagListComponent.js
const TagListComponent = function Basic({
selectedItemsIndices,
}) {
return (
<div>
{selectedItemsIndices.map((selectedItemIndex, index) => {
return (
<div>
<span>{data[selectedItemIndex]}</span>
</div>
);
})}
</div>
);
};
export default TagListComponent;
In the <span/> tag above, put whatever styling you'd like for the tag itself.
Ant modal appears few times, but if i remove map function inside the modal then its works fine its appears only one time.
what may be the issue? please help me to solve this issue (Modal triggering/appears few times)
Ant modal code follows:
const [visibleVoucherModal, setVisibleVoucherModal] = useState(false);
const showVoucherModal = () => {
setVisibleVoucherModal(true)
}
const closeVoucherModal = () => {
setVisibleVoucherModal(false);
};
const SavedVoucherModal = () => {
return (
<Modal title="Select Any Voucher" visible={visibleVoucherModal} onCancel={closeVoucherModal} footer={null} >
{savedVoucherList.length >= 1 ?
<div>
<Checkbox.Group className="w-100" onChange={checkBox} value={voucherVal} >
{savedVoucherList.map((item, i) => {
return (
<div key={i}>
<Checkbox value={item.Id} className="w-100" onChange={e => { handleCheckBox(e, i) }}
checked={checked[i] || true} disabled={!checked[i] && disabled} >
<div className="pl-4">
<List.Item style={{ marginTop: "-35px" }} className="py-0">
<List.Item.Meta
// avatar={<img src={item.Photo == '' ? 'd' : item.Photo} alt="" width="72" />}
title={<span style={{ fontSize: "12px" }}>{item.Name}</span>}
description={<div className="small">Expiry Date: <br />{item.ExpireDate.slice(0, 10)}</div>}
/>
<div className="pt-1">
<p className="font-weight-bold text-primary mb-0">{numberFormat(item.Price)}</p>
<small><del>{numberFormat(item.OldPrice)}</del></small>
</div>
</List.Item>
</div>
</Checkbox>
<Divider className="m-0 p-0 mb-4" />
</div>)
})}
</Checkbox.Group>
</div> :
<p className="mb-4">Oops, there is no voucher applicable to this order</p>
}
<Button type="primary" className="font-weight-bold btn-round" onClick={closeVoucherModal}>Close</Button>
</Modal>
)
}
Button:
<Button type="default" className="text-primary border-right-0 border-left-0" size="large" onClick={showVoucherModal}>Redeem Voucher</Button>
I am new to reactJS. I have a select option.
I want to add delete and edit button inside the select option.
Here's the code i tried,
<Select
style={{ width: 240 }}
placeholder="Choose Web Analytics Configuration"
dropdownRender={menu => (
<div>
{menu}
<Divider style={{ margin: '4px 0' }} />
<div
style={{ padding: '4px 8px', cursor: 'pointer' }}
onMouseDown={e => e.preventDefault()}
onClick={this.openModal.bind(this)}
>
<Icon type="plus" /> Add Database
</div>
</div>
)}
>
{dbConfigList.map(item => (
<Option key={item}>{item}
<Icon onClick={this.editFun.bind(this)} type="edit" style={{ fontSize: '20px', color: 'green' }} theme="outlined" />
<Icon onClick={this.deleteFun.bind(this)} type="delete" style={{ fontSize: '20px', color: '#CC160B' }} theme="outlined" />
</Option>
))}
</Select>
I have successfully added the buttons.
Here it looks like:
But when the User selects the option, the edit and delete button appears in the select box. I only needed the name to be shown and not buttons.
Here it looks like,
Help me with some solutions to fix it and inside the dropdown menu of every each option, I want the edit and delete buttons to be appeared at the right corner. Now it was appearing after the text. i can use margin for that. but every name size differs. Ex. Some names will be 6 characters and some will be more than 6 characters. Help me with some solutions.
Check the below example, you need to prevent your <Icon> as I had done for '+ -'
should be like:
{
dbConfigList.map(item => (
<Option key={item}>
{item}
{selectedValue !== item // you need to add state
<Icon
onClick={this.editFun.bind(this)}
type="edit"
style={{ fontSize: "20px", color: "green" }}
theme="outlined"
/>
<Icon
onClick={this.deleteFun.bind(this)}
type="delete"
style={{ fontSize: "20px", color: "#CC160B" }}
theme="outlined"
/> : null}
</Option>
));
}
class App extends React.Component {
constructor() {
super();
this.state = {
optionsdata : [
{key:'101',value:'Lion'},
{key:'102',value:'Giraffe'},
{key:'103',value:'Zebra'},
{key:'104',value:'Hippo'},
{key:'105',value:'Penguin'}
],
selectedValue: null // store selected value
}
}
handleChange = (e) => {
console.log(e.target.value);
var value = this.state.optionsdata.filter(function(item) {
return item.key == e.target.value
})
this.setState({ selectedValue: value[0].value }); // set state
console.log(value[0].value);
}
render() {
const { selectedValue } = this.state;
return (
<select onChange={this.handleChange}>
{this.state.optionsdata.map(function(data, key) { return (
<option key={key} value={data.key}>{data.value} {selectedValue !== data.value ? '+ -' : null } </option> )
})}
</select>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/15.0.1/react-dom.min.js"></script>
<div id="app"></div>
Demo App: https://codesandbox.io/s/button-in-select-menuitem-reactjs-demo-1zyvj
I solved this problem in material-Ui design by using onOpen={() => setIconVisibile(true)} and onClose={() => setIconVisibile(false)} callbacks to set the visibility of icons.
const [isIconVisibile, setIconVisibile] = React.useState(false);
<Select
onOpen={() => setIconVisibile(true)}
onClose={() => setIconVisibile(false)}
>
<MenuItem value={1}>
{"ONE"}
{isIconVisibile ? (
<ListItemSecondaryAction variant="outlined">
<IconButton edge="end" aria-label="delete">
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
) : null}
</MenuItem>
</Select>
This is my first time to develop a react application.
I want to add a button aligned with the pagination buttons (Previous, Next) in for navigating tables.
I've tried to do this by duplicating the Table.js that points to different component (index.js). Is there a more efficient way to add the button using 1 Table.js file, and adding a variable in the index.js as an indicator for displaying/hiding the added button? Or have a better implementation? Thank you
Display Button 1 on component 1 only
Display Button 2 on component 2 only
pagination = pageOptions.length ? (
<Pagination {...getRowProps()}>
<Cell>
<Button onClick={() => previousPage()} disabled={!canPreviousPage}>
Previous
</Button>{" "}
<Button onClick={() => nextPage()} disabled={!canNextPage}>
Next
</Button>{" "}
<span>
Page{" "}
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{" "}
</span>
<span>
| Go to page:{" "}
<Input
type="number"
defaultValue={pageIndex + 1}
onChange={e => {
const page = e.target.value ? Number(e.target.value) - 1 : 0;
gotoPage(page);
}}
style={{ width: "100px" }}
/>
</span>{" "}
<Select
value={pageSize}
onChange={e => {
setPageSize(Number(e.target.value));
}}
>
{[10, 20, 30, 40, 50].map(pageSize => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</Select>{" "}
**<Button onClick={() => test1()}>BUTTON1</Button> -> Display only on component 1
<Button onClick={() => test2()}>BUTTON2</Button> -> Display only on component 2**
</Cell>
</Pagination>
) : null;
return (
<div>
<Table {...getTableProps()}>
{headerGroups.map(headerGroup => (
<HeaderRow {...headerGroup.getRowProps()}>
{headerGroup.headers.map(column => (
<Header
{...column.getHeaderProps()}
sorted={column.sorted}
sortedDesc={column.sortedDesc}
sortedIndex={column.sortedIndex}
>
<div>
<span {...column.getSortByToggleProps()}>
{column.render("Header")}
</span>{" "}
{/* {column.canGroupBy ? (
<Emoji {...column.getGroupByToggleProps()}>
{column.grouped ? "🛑" : "👊"}
</Emoji>
) : null} */}
</div>
{column.canFilter ? <div>{column.render("Filter")}</div> : null}
</Header>
))}
</HeaderRow>
))}
{tableBody}
<Row {...getRowProps()}>
{loading ? (
<Cell>
<strong>Loading...</strong>
</Cell>
) : (
<Cell>{rows.length} Total Records</Cell>
)}
</Row>
{pagination}
</Table>
</div>
);
You should use state in this case.
And change the state from witch component is mounted right now. You can use the react lifecycle methods componentDidMount(){} and componentWillUnmount() {} for setting state.
And then just check the state and add the button.
State and Lifecycle - check the docs, it will answer a lot