I'm trying to pass a class only to the checked radio button in my component, but with my existing code all the radio buttons receive the class. Any advice?
export default function RadioGroup({options}){
const [isChecked, setIsChecked] = useState(false);
return(
<>
{options.map(option => {
return (
<Radio
radioID={option.id}
radioName={name}
radioLabel={option.label}
radioClass={` ${isChecked ? "bg-blue" : ""}`}
onChange={() => setIsChecked((prev) => !prev)}
/>
)
})}
</>
);
}
Instead of passing Boolean in setIsChecked, you have to pass the id then you can add the bg-blue class on id base, as you can see the below code.
export default function RadioGroup({options}){
const [isChecked, setIsChecked] = useState(null);
return(
<>
{options.map(option => {
return (
<Radio
radioID={option.id}
radioName={name}
radioLabel={option.label}
radioClass={` ${(isChecked === option.id) ? "bg-blue" : ""}`}
onChange={() => setIsChecked(option.id)}
/>
)
})}
</>
);
}
Related
I need to dynamically generate multiple divs with input-field in it, so the user can add values in it.
By clicking the button user can create as many divs with input-field as he needs
By typing values total amount must be calculated in real-time.
My problem is how to get all input values into one array so I could calculate the total.
I just need any kind of example how to get those values in one array.
*Note I use functional components.
const ReceiptDetails = () => {
const [isOpen, setIsOpen] = useState(false);
const [selectedOption, setSelectedOption] = useState(null);
const [expenseList, setExpenseList] = useState([]);
const [expenseTotal, setExpenseTotal] = useState(0);
const options = ['Food', 'Entertaiment', 'Houseware'];
const onOptionClicked = (option) => {
setSelectedOption(option);
setIsOpen(false);
};
const formHandler = (e) => {
e.preventDefault();
if (selectedOption !== null) {
setExpenseList(
expenseList.concat(
<ExpenseDetails key={expenseList.length} calcTotal={calcTotal} />
)
);
}
return;
};
return (
<Container>
<Form onSubmit={formHandler}>
<div>
<DropDownHeader onClick={() => setIsOpen(!isOpen)} required>
{selectedOption || 'Select'}
<span className='arrow-head'>⌄</span>
</DropDownHeader>
{isOpen && (
<div>
<DropDownList>
{options.map((option, indx) => (
<ListItem
onClick={() => onOptionClicked(option)}
value={option}
key={indx}
>
{option}
</ListItem>
))}
</DropDownList>
</div>
)}
</div>
<Button type='secondary'>Add expense</Button>
</Form>
{expenseList}
{expenseList.length !== 0 && (
<ExpenseTotal>Total {expenseTotal}</ExpenseTotal>
)}
</Container>
);
};
export default ReceiptDetails;
const ExpenseDetails = () => {
const [expense, setExpense] = useState(0);
return (
<DetailsContainer>
<div className='first'>
<FirstInput type='text' placeholder='Expense name' />
<SecondInput
type='number'
placeholder='€0.00'
value={expense}
onChange={(e) => setExpense(e.target.value)}
/>
</div>
</DetailsContainer>
);
};
export default ExpenseDetails;
how about you use a context? That will allow you to keep and share your state between the components. I'd do something like here. Keep the array of expenses at the context level and the state of the form. From that, you can get the values and display them in the child components.
But it would be best to read the documentation first ;)
My modal causes a crash whenever I close it. The modal contains picture details and is displaying them.
The error message I am getting is:
"TypeError: Cannot read properties of undefined (reading 'name')"
Although the above prop worked fine while the modal was open. A thing I noted is that whenever the modal contains only the below paragraph with the id, it works fine and closes without any issues.
<p>{modalContent.id}</p>
Where the FavoritesModal gets its props
const FavoritesSidebar = () => {
const [modalOpen, setModalOpen] = useState(false)
const [modalContent, setModalContent] = useState(null)
const favoriteList = useSelector((state) => state.favoriteList)
const toggleModal = (arg) => {
setModalOpen(!modalOpen)
setModalContent(arg)
}
return (
<Container className="favorites-sidebar">
{/* {modalContent ? <p>yes</p> : <p>no</p>} */}
{favoriteList.length > 0 ? <h4>Your favorited pictures</h4> : null}
{favoriteList.length > 0 &&
favoriteList.map((item) => (
<Row key={item.id} className="favorite-item">
<Col onClick={() => toggleModal(item)}>
</Col>
</Row>
))}
{modalContent ? (
<FavoritesModal
modalOpen={modalOpen}
modalContent={modalContent}
toggleModal={toggleModal}
/>
) : null}
</Container>
)
}
export default FavoritesSidebar
FavoritesModal
const FavoritesModal = (props) => {
const { modalOpen, modalContent, toggleModal } = props
const dispatch = useDispatch()
if (modalContent)
return (
<Modal
isOpen={modalOpen}
toggle={toggleModal}
centered
fullscreen=""
size="xl"
>
<ModalHeader>
<p>
{modalContent.description !== null
? `${modalContent.description} by ${modalContent.user.name}`
: `Taken by: ${modalContent.user.name}`}
</p>
<p>{modalContent.id}</p>
</ModalHeader>
<ModalBody>
<img src={modalContent.urls.regular} alt={modalContent.description} />
</ModalBody>
<ModalFooter>
<Button
color="danger"
onClick={() => dispatch(removeFromFavorites(modalContent.id))}
>
Remove from favorites
</Button>
</ModalFooter>
</Modal>
)
else return null
}
export default FavoritesModal
Rewriting the toggleModal functioned solved the issue
const toggleModal = (arg) => {
if (modalOpen) {
setModalOpen(!modalOpen)
setModalContent(null)
} else {
setModalOpen(!modalOpen)
setModalContent(arg)
}
}
I created a component (sort of popup box) which displays a sign of horoscope, there’s an image and description. The popup box works correctly. I added a button ‘more’ to see more description, so I used a useState for it, but it doesn’t work, when I click on it doesn't show the rest of the text.
Thanks for your help !
const Modal = ({
children, visible, hide, fermer, more,
}) => {
const popup = `popup ${visible ? 'block' : 'hidden'}`;
return (
<div className={popup}>
{fermer ? null : (
<button className="close" onClick={hide} type="button">X</button>
)}
{children}
<button className="more" onClick={more} type="button">more</button>
</div>
);
};
export default Modal;
import './App.css';
import { useState } from 'react';
import Element from './Element';
import Modal from './Modal';
import Bd from './Bd';
function App() {
const bd = Bd.map((element) => (
<Element
nom={element.nom}
image={element.image}
description={element.description}
modulo={element.modulo}
/>
));
const [year, setYear] = useState('');
function handleChange(event) {
setYear(event.target.value);
}
const [signe, setSigne] = useState([]);
const [vis, setVis] = useState(false);
const [desc, setDesc] = useState(true);
function handleSubmit() {
setVis(true);
const yearModulo = Number(year) % 12;
Bd.map((element) => (
yearModulo === element.modulo ? setSigne(
[<h1>{element.nom}</h1>,
<div>{element.description.substr(0, 150)}</div>,
desc ? <div />
: <div>{element.description.substr(150, 600)}</div>,
<img src={`/images/${element.image}`} alt="" />,
],
) : false
));
}
return (
<div>
<div>
<input
className="text-center font-bold"
type="number"
id="year"
name="year"
value={year}
onChange={handleChange}
/>
<button type="submit" onClick={handleSubmit}>Valider</button>
</div>
<div className="flex flex-wrap">{bd}</div>
<Modal
visible={vis}
hide={() => setVis(false)}
more={() => setDesc(false)}
>
<div>
<div>{signe}</div>
</div>
</Modal>
</div>
);
}
export default App;
I would avoid storing in a local state a component (setSigne([<h1>{element.nom}</h1>,...). Prefer storing in the state the values that cannot be computed from other existing states, and generate the elements at rendering.
const [signe, setSigne] = useState(null);
function handleSubmit() {
setVis(true);
const yearModulo = Number(year) % 12;
setSigne(Bd.find(element => yearModulo === element.modulo));
}
// ...
<div>
{signe && <div>
<h1>{signe.nom}</h1>
<div>{signe.description.substr(0, 150)}</div>
{desc ? <div /> : <div>{signe.description.substr(150, 600)}</div>}
<img src={`/images/${signe.image}`} alt="" />
</div>}
</div>
Also, don’t forget to add a key prop when generating elements from an array:
const bd = Bd.map(element => (
<Element
key={element.nom}
// ...
I am trying to change the innerhtml of an element in react, my solution is obviously not ideal since i'm manipulating the DOM directly. How can I write this in the correct way? Would it be better to useState or useRef? Basically, if plus is selected then turn into minus and if minus is selected then turn into plus. Each button is unique and there are multiple buttons
function App() {
const [tags, setTags] = useState([]);
const displayScores = (e) => {
if (e.target.innerHTML == "+") {
e.target.innerHTML = "-";
} else {
e.target.innerHTML = "+";
}
};
return (
<>
{tags.filter((t) => t.studentEmail == students.email).map((tag, index) => {
return (
<p key={index} className="student-tags">
{tag.value}
</p>
);
})}
<input
onKeyDown={addTag.bind(this, students.email)}
className="student-tag"
type="text"
placeholder="Add a tag"
/>
</div>
</div>
<button onClick={displayScores} className="expand-btn">+</button>
</div>
);
})}
</>
)
}
export default App;
I don't think you need to access the DOM for this at all. You could use some React state (boolean) that determines if scores are displayed, and then render a "+" or "-" in the button based off of that boolean.
// Your state
const [scoresDisplayed, setScoresDisplayed] = useState(false);
// Function to update state
const displayScores = () => {
// Toggle the boolean state
setScoresDisplayed(state => !state);
};
// Conditionally render button text
<button onClick={displayScores} className="expand-btn">
{scoresDisplayed ? "-" : "+"}
</button>
in React you should not work with something like inner html,there much better ways like below :
function App() {
const [tags, setTags] = useState([]);
const [buttonLabel, setButtonLabel] = useState("+");
const toggleButtonLabel = () => {
setButtonLabel(buttonLabel === "+" ? "-" : "+");
};
return (
<>
{tags
.filter((t) => t.studentEmail == students.email)
.map((tag, index) => {
return (
<p key={index} className="student-tags">
{tag.value}
</p>
);
})}
<input
onKeyDown={addTag.bind(this, students.email)}
className="student-tag"
type="text"
placeholder="Add a tag"
/>
<button onClick={toggleButtonLabel} className="expand-btn">
{buttonLabel}
</button>
</>
);
}
export default App;
I have a nav menu built with material-ui/core in Navbar.
I use useRef to track the position of clicked button on toggle menu close.
anchorRef.current.contains(event.target)
And I am getting 'Uncaught TypeError: anchorRef.current.contains is not a function' .
I tried 'Object.values(anchorRef.current).includes(event.target)' instead, it always returns false.
-- update --
anchorRef.current.props Object.
withStyles {
props:{
aria-haspopup: "true"
aria-owns: undefined
children: "계정"
className: "nav-menu--btn"
onClic: f onClick()
get ref: f()
isReactWarning: true
arguments: (...)
caller: (...)
length: 0
name: "warnAboutAccessingRef"
...
}, context{...}, refs{...}, ...}
ToggleMenuList
const ToggleMenuList = ({ navAdminList, navAdminItems, classes }) => {
const [activeId, setActiveId] = useState(null);
const anchorRef = useRef(null);
const handleToggle = id => {
setActiveId(id);
};
const handleClose = event => {
if (anchorRef.current && anchorRef.current.contains(event.target)) {
return;
}
setActiveId(null);
};
return (
<React.Fragment>
<div className={`nav-menu--admin ${classes.root}`}>
{navAdminList.map(e => (
<div key={e.id}>
<Button
ref={anchorRef}
aria-owns={activeId === e.id ? 'menu-list-grow' : undefined}
aria-haspopup="true"
onClick={() => handleToggle(e.id)}
>
{e.name}
</Button>
{activeId === e.id && (
<ToggleMenuItems
id={e.id}
activeId={activeId}
handleClose={handleClose}
anchorRef={anchorRef}
items={navAdminItems[e.id]}
/>
)}
</div>
))}
</div>
</React.Fragment>
);
};
export default withStyles(styles)(ToggleMenuList);
ToggleMenuItems
const ToggleMenuItems = ({
listId,
activeId,
handleClose,
anchorRef,
items,
}) => {
const isOpen = activeId === listId;
const leftSideMenu = activeId === 3 || activeId === 4 ? 'leftSideMenu' : '';
return (
<Popper
open={isOpen}
anchorEl={anchorRef.current}
keepMounted
transition
disablePortal
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin:
placement === 'bottom' ? 'center top' : 'center bottom',
}}
className={`toggle-menu ${leftSideMenu}`}
>
<Paper id="menu-list-grow">
<ClickAwayListener
onClickAway={handleClose}
>
<MenuList className="toggle-menu--list">
{items.map(e => (
<MenuItem
key={e.id}
className="toggle-menu--item"
onClick={handleClose}
>
<Link
to={e.to}
className="anchor td-none c-text1 toggle-menu--link"
>
{e.name}
</Link>
</MenuItem>
))}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
);
};
export default ToggleMenuItems;
react: ^16.8.6
react-dom: ^16.8.6
react-router-dom: ^4.3.1
#material-ui/core: ^3.1.2
I assume your ToggleMenuItems sets up global(document-level?) event listener on click to collapse Menu on clicking somewhere outside.
And you have a sibling button element. Clicking on that you want to keep menu expanded, right? So that was the point to use .contains in onClick to check if we are clicked outside of ToggleMenuItems but in scope of specific Button. The reason why it does not work: <Button> is custom class-based React component so it returns React component instance in ref. And it does not have any DOM-specific methods like .contains
You can rework you current approach: just stop bubbling event in case Button has been clicked. It would stop global event handler set by ToggleMenuItems to react.
const stopPropagation = (event) => event.stopPropagation();
const ToggleMenuList = ({ navAdminList, navAdminItems, classes }) => {
const [activeId, setActiveId] = useState(null);
const anchorRef = useRef(null);
const handleToggle = id => {
setActiveId(id);
};
const handleClose = event => {
setActiveId(null);
};
return (
<React.Fragment>
<div className={`nav-menu--admin ${classes.root}`}>
{navAdminList.map(e => (
<div key={e.id}>
<div onClick={stopPropagation}>
<Button
aria-owns={activeId === e.id ? 'menu-list-grow' : undefined}
aria-haspopup="true"
onClick={() => handleToggle(e.id)}
>
{e.name}
</Button>
</div>
{activeId === e.id && (
<ToggleMenuItems
id={e.id}
activeId={activeId}
handleClose={handleClose}
anchorRef={anchorRef}
items={navAdminItems[e.id]}
/>
)}
</div>
))}
</div>
</React.Fragment>
);
};
export default withStyles(styles)(ToggleMenuList);
I've put stopPropagation handler outside since it does not depend on any internal variable.