I have created one custom component, alternative of Select component, on click shows ul and on click hide ul.
when I click I can set value to the state inside function, but i want to access the value in parent component.
so my component is
const [showMenu, setShowMenu] = useState(false);
const [value, setValue] = useState();
return (
<>
<button
className='btn'
onClick={() =>
showMenu ? setShowMenu(false) : setShowMenu(true)
}>
{props.name}
</button>
<ul className={showMenu ? "" : "hide"}>
{props.listsa.map((element) => {
return (
<li
key={element.key}
value={element.value}
onClick={(e) => {
setValue(e.target.value);
}}>
{element.label}
</li>
);
})}
</ul>
</>
I want to access value mentioned above functional component in parent component, which is my app.js
as shown below, this is return method of parent component.
<div className='App'>
{/* <Main /> */}
<OptionsComponent name='ABC Menu' listsa={abc} />
{/* here I want to use that value to perfom operations/ also on change it should show changed value */}
</div>
I tried using localStorage.setItem("value":value) it works but that will use browser memory so I am looking for alternative way.
I tried exporting variable, it shows undefined, also I tried making varibale global, it works but doesnt reflect change.
any help will be appreciated
You just need to bring the state up and pass it down, instead:
const [value, setValue] = useState();
return (
<div className='App'>
{/* <Main /> */}
<OptionsComponent name='ABC Menu' listsa={abc} value={value} setValue={setValue}/>
</div>
And
const [showMenu, setShowMenu] = useState(false);
return (
<>
<button
className='btn'
onClick={() =>
showMenu ? setShowMenu(false) : setShowMenu(true)
}>
{props.name}
</button>
<ul className={showMenu ? "" : "hide"}>
{props.listsa.map((element) => {
return (
<li
key={element.key}
value={element.value}
onClick={(e) => {
props.setValue(e.target.value);
}}>
{element.label}
</li>
);
})}
</ul>
</>
);
Related
Okay there is definitely a quick solution for this I just can't figure out.
Just a description of what I am trying to do:
Whenever I hover over a certain card, I would like to see the description of that item and only that item. But instead what's obviously happening, as you can see from my code, is every single cards description is showing.
I rewrote a simpler version of the code by taking out any unnecessary pieces. Everything is imported correctly, styling and classNames were removed as well.
export function Items() {
const [items, setItems] = useState([])
const [isHovering, setIsHovering] = useState(false)
useEffect(() => {
setItems(Data)
}, [])
function handleMouseOver() {
setIsHovering(true)
}
function handleMouseOut() {
setIsHovering(false)
}
return(
<div>
{items.map(item => {
return(
<Card onMouseOver={handleMouseOver} onMouseOut={handleMouseOut} key={item.id}>
{isHovering ?
<Card.Body>
<p>{item.item_description}</p>
</Card.Body>
:
<Card.Body>
</Card.Body>
}
<Card.Footer>
</Card.Footer>
</Card>
)
})}
</div>
)
}
As far as I can see you don't need to put this logic into parent component, and also it makes everything more complex, since it's hard to manage hovering. I would create new chlid component and manage this state out there internally.
export function Item({item}) {
const [isHovering, setIsHovering] = useState(false);
useEffect(() => {
setItems(Data);
}, []);
function handleMouseOver() {
setIsHovering(true);
}
function handleMouseOut() {
setIsHovering(false);
}
return (
<Card onMouseOver={handleMouseOver} onMouseOut={handleMouseOut}>
{isHovering ? (
<Card.Body>
<p>{item.item_description}</p>
</Card.Body>
) : (
<Card.Body></Card.Body>
)}
<Card.Footer></Card.Footer>
</Card>
);
}
export function Items() {
const [items, setItems] = useState([]);
return (
<div>
{items.map(item => (
<Item key={item.id} item={item} />
))}
</div>
);
}
Your "isHovering" state should also be an array, where you store the hover state for every card. Then on hover set "isHovering" to true only for the right card.
I'm learning how to do dropdown menu from a tutorial.
There are some parts that I don't know..
why pass {selected, setSelected} as a object props to Dropdown component instead of using selected, setSletected directly? I've tried and it throws an error.
at this part
{options.map((option) => (
<div
onClick={(e) => {
setSelected(option);
}}
>
the tutorial initially
used onClick={(e) => {setSelected(e.target.textContent);}}
why have to change e.target.textContent into option?
how do program know which item is selected exactly by option without using e.target.textContent?
function App() {
const [selected, setSelected] = React.useState("Choose One");
return (
<div>
{/* custom dropdown menu */}
<Dropdown selected={selected} setSelected={setSelected} />
{selected}
</div>
);
}
function Dropdown({ selected, setSelected }) {
const [isActive, setIsActive] = React.useState(false);
const options = ["React", "Vue", "Angular"];
return (
<div onClick={(e) => setIsActive(!isActive)}>
{isActive && (
<div>
{options.map((option) => (
<div
onClick={(e) => {
setSelected(option);
}}
></div>
))}
</div>
)}
</div>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
So I have a toggle looking like this (see below), but the page always re-render the whole thing on the first time I click on the toggle.
export default function Toggle({isChecked, label}: Props) {
return (
<Wrapper>
<Switch isChecked={isChecked}>
<span />
</Switch>
{label}
</Wrapper>
)
}
Then another component which is using this Toggle component
export default function ToggleBox({isChecked, label, children}: Props) {
return (
<Wrapper>
<Toggle isChecked={isChecked} label={label} />
<Content>{children}</Content>
</Wrapper>
)
}
There is a layout
export default function Layout({...someprops bla bla, children}: Props) {
<Wrapper>
<DesktopImg>
<ImageWrapper>
<Image src={image.url} alt={`${headline} image`} layout="fill" />
</ImageWrapper>
</DesktopImg>
<div>
<Content>
{back && backButton}
<MobileImg>
<Image
src={image.url}
alt={`${headline} image`}
width={image.width}
height={image.height}
/>
</MobileImg>
{headline}
<P gutterSize="medium">{description}</P>
</Content>
<ChildrenContainer>{children}</ChildrenContainer>
</div>
</Wrapper>
}
Then finally the page which use the ToggleBox.
export default function Index({isChecked, label, children}: Props) {
const [check, setCheck] = useState(false)
return (
<Layout>
<div onClick={() => setCheck(!check)}>
<ToggleBox label="some label..." isChecked={check}>
//sometext..
</ToggleBox>
</div>
<Button onClick={nextPage} disabled={!check}>
Next
</Button>
</Layout>
)
}
I kinda tried to use the React.memo method but it doesnt seem to work. Any suggestion to make the page not re-render the whole thing but just the toggle?
Move your state further down the tree, you want it to be as close to the component(s) it impacts as possible, again this will probably require breaking out into smaller components, for example, break out the following into a seperate component -
const NewToggleComponent = () => {
const [check, setCheck] = useState(false)
return (
<div onClick={() => setCheck(!check)}>
<ToggleBox label="some label..." isChecked={check}>
//sometext..
</ToggleBox>
</div>
<Button onClick={nextPage} disabled={!check}>
Next
</Button>
)
}
remove state from the top level component, and use this component in your top level component -
...
<NewToggleComponent />
...
I have two seperate components namely Header and Modal. There is a button in Navbar that has an onClick function attached to it. By clicking it the state changes and hence the state is passed to modal as props which then triggers it. The state is then set to false again. However if i click on the button again, the modal does not appear. I have tried many different thing even with useEffects but nothing worked.
Header code
const Header = () => {
const [modal, setModal] = useState(false)
return(
<div>
{modal ? <ModalProduct showModal={true} /> : null}
<ul class="nav navbar-nav ml-auto">
<li><Button variant="danger" style={{ marginTop: '8px' }} onClick={() => setModal(true)}>Add
new product</Button></li></ul>)
</div>)
And for the Modal Component I have
export default function ModalProduct(props) {
const [show, setShow] = useState(props.showModal);
const handleClose = () =>
setShow(false);
return (
< div >
<Modal show={show} onHide={handleClose}>
...
</Modal>
</div >
);
}
There is something related to do with hooks, it gets true for the first time popping the modal and then changes to false but then does not become true again.
Point the onClick to a function that will handle the switching of the state by if true->false and false->true.
const Header = () => {
const [modal, setModal] = useState(false)
const triggerModel = () => {
setModal(!modal)
}
return(
<div>
{modal ? <ModalProduct showModal={triggerModel} /> : null}
<ul class="nav navbar-nav ml-auto">
<li><Button variant="danger" style={{ marginTop: '8px' }} onClick={() => setModal(true)}>Add
new product</Button></li></ul>)
</div>)
You are using 2 states for the same thing (showing/hiding the modal). You only need one state for that. Remove the state from your ModalProduct component and just use the props from your parent component Header to handle the modal. I also refactored your code to make it more concise and readable.
const Header = () => {
const [showModal, setShowModal] = useState(false);
return(
<div>
<ModalProduct showModal={modal} handleClose={()=> setShowModal(false)} />
<ul class="nav navbar-nav ml-auto">
<li>
<Button variant="danger" style={{ marginTop: '8px' }} onClick={()=>setShowModal(true)}>
Add new product
</Button>
</li>
</ul>
</div>
export default function ModalProduct(props) {
return (
<Modal show={props.showModal} onHide={props.handleClose}>
...
</Modal>
);
}
I have a small app fetching Movies. The component tree isn't very deep. I have state in App.js and it passes data to the Movies.js component. Now, Movies.js is just a container to render the ul list of Movies.
App.js passing data to Movies
<Movies
modalData={this.state.modalData}
toggleModal={this.openModal}
isOpen={this.state.modalIsOpen}
closeModal={this.closeModal}
items={this.state.data}
getMovieInfo={this.getMovieInfo}
addToFavorites={this.addToFavorites}
/>
Movies.js
const Movies = props => {
const { items, getMovieInfo, addToFavorites, isOpen, toggleModal, closeModal, modalData } = props;
const listItems = items.map((item, id) => (
<Movie
key={id}
id={id}
imdbID={item.imdbID}
title={item.Title}
poster={item.Poster}
getMovieInfo={getMovieInfo}
addToFavorites={addToFavorites}
imdbid={item.imdbID}
isOpen={isOpen}
toggleModal={toggleModal}
closeModal={closeModal}
modalData={modalData}
/>
));
return <ul id="movie-container">{listItems}</ul>;
};
and finally Movies.js which is just an item that is rendered in the Movie component. I tried using destructuring to keep the items prop for the Movies component because it needs that to map over and then ...props to Movie. I cant seem to make this work.
My app works fine but this is 'prop-drilling' and I think context may not be appropriate because this is not a deep tree. Higher Order Components, I don't understand yet. What can I do to make this better?
Movie.js
const Movie = props => {
const {
id,
imdbID,
title,
poster,
getMovieInfo,
addToFavorites,
isOpen,
toggleModal,
closeModal,
modalData,
} = props;
return (
<React.Fragment>
<li className="movie" id={id}>
<img srcSet={poster} alt={title} imdbid={imdbID} />
<div className="movie-title">{title}</div>
<div className="button-container">
<button
type="submit"
className="button-small"
onClick={toggleModal}
imdbid={imdbID}
title={title}
id={id}>
{' '}
Info!!
</button>
<button
type="submit"
className="button-small"
onClick={addToFavorites}
imdbid={imdbID}
id={id}>
{' '}
Fav
</button>
</div>
</li>
<Modal className="modal" selector="#modal-root" isOpen={isOpen} onClick={closeModal}>
<div className="modal-1">
You can do like:
const Movies = ({ items, ...other}) => {
<Movie {...other} />
Or,
const { items, ...other } = props
<Movie {...other} />
Your code in example:
const Movies = ({ items, ...other}) => {
const listItems = items.map((item, id) => (
<Movie
key={id}
id={id}
{...other}
/>
));
return <ul id="movie-container">{listItems}</ul>;
};
Your update made me afraid. You need to destructure all otherwise it will go even longer like id={other.id}. If you feel it's good rather, then do:
const { items, ...other } = props
// now when you use other props like imdbid
imdbid={other.imdbid}