React useState hook only updates once onClick - reactjs

I am trying to toggle true/false in my component using useState but I've noticed it only toggles one time and does not go back and forth. On clicking the component, it toggles true but then it won't toggle back to false. Any ideas on how to solve?
const [sound, setSound] = useState(false);
return (
<div>
<ReactPlayer
...
muted={sound}
onClick={() => {
setSound(!sound);
console.log("on click", sound);
}}
/>
</div>
)
EDIT
Thanks for the replies, I think the issue was the anon function, I solved it by doing this
onClick={() => {
setSound((sound) => !sound);
console.log("on click", sound);
}}

import React, {useState} from 'react';
import './App.css';
const ReactPlayer = ({muted, onClick}: {muted: boolean, onClick: () => any}) => {
return (
<div>
<button onClick={onClick}>Test</button>
<p>{muted.toString()}</p>
</div>
)
}
function App() {
const [sound, setSound] = useState(false);
return (
<div className="App">
<ReactPlayer
muted={sound}
onClick={() => {
setSound(!sound);
console.log("on click", sound);
}}
/>
</div>
);
}
export default App;
This code works perfectly, I don't know what you have in your ReactPlayer component, but this should work

As of Chrome 66, videos must be muted in order to play automatically. Some players, like Facebook, cannot be unmuted until the user interacts with the video, so you may want to enable controls to allow users to unmute videos themselves. Please set muted={true}. see docs for more info
Check the live code here in sandbox link
import ReactPlayer from "react-player";
import React, { useState } from "react";
// Render a YouTube video player
export default function App() {
const [play, setPlay] = useState(true);
return (
<div className="App">
<ReactPlayer
muted={play}
url="https://www.youtube.com/watch?v=9DDX3US3kss"
/>
Click to Mute or Unmute --
<button style={{ margin: "15px" }} onClick={() => setPlay(!play)}>
Mute/UnMute
</button>
</div>
);
}

Related

Display data in modal

I would like to pass several data in a modal. My modal works fine but does not display the data I want. I would like to display the data BY ID, but I get all the data. This is my code :
App.js :
import React, { useState, useEffect } from "react";
import Modal from "./Modal";
import PlayCircleIcon from '#mui/icons-material/PlayCircle';
const App = (props) => {
const [image, setImage] = useState([])
return (
<div>
{image.map((image) => {
return (
<div key={image.id}>
<img src={...}
alt={image.title}
/>
<ExpandMoreIcon onClick={handleClick} />
</div>
)
})}
</div>
);
}
export default App;
Modal.js :
import React from 'react';
const Modal = ({ showModal, image }) => {
console.log("result", image);
return (
<div className="modal" >
<div className='modal__content'>
<h1 className='modal__title'>{image.title}</h1>
<p className='modal__description'>{image.description}</h1>
</div>
</div>
);
}
export default Modal;
I think the problem comes from here image={image} in App.js because I get 8 tables in console.log("result", movie);
{showModal && <Modal showModal={handleClick} image={image} />}
Your problem is your modal state is true/false value, so that's why once you trigger showModal, it will show all modals according to your number of images
The fix can be
Note that I modified your state name to align with new state values
const [modalImage, setModalImage] = useState(); //undefined as no image selected
const handleClick = (imageId) => {
setModalImage(imageId);
};
Your elements will be like below
<ExpandMoreIcon onClick={() => handleClick(image.id)} />
{modalImage === image.id && <Modal showModal={() => handleClick(image.id)} image={image} />}
If you want to close the modal, you can reset modalImage state
onClick={() => handleClick(undefined)}
I just noticed that you also can move your modal out of the image loop, and potentially you can pass the entire image object to the modal too
The full implementation
import React, { useState, useEffect } from "react";
import Modal from "./Modal";
import PlayCircleIcon from '#mui/icons-material/PlayCircle';
const App = (props) => {
const [image, setImage] = useState([])
const [modalImage, setModalImage] = useState(); //no image selected
const handleClick = (image) => {
setModalImage(image);
};
useEffect(() => {
...
}, []);
return (
<div>
{image.map((image) => {
return (
<div key={image.id}>
<img src={...}
alt={image.title}
/>
<ExpandMoreIcon onClick={() => handleClick(image)} />
</div>
)
})}
{modalImage && <Modal showModal={() => handleClick(modalImage)} image={modalImage} />}
</div>
);
}
export default App;
Both the map item and the image state array are named image in your code here;
image.map((image) => {
/// your code
})
which could be the ambiguity that led to showing the whole array of data instead of one image in the array.

Why won't my history.push code for Button onClick handler work?

I've only been working with React.js for a month or so and I hope someone can point me towards my errors so I can learn more.
I've created a reusable button component, but during testing, while the button displays correctly and I can change the value correctly - the onClick function is not working. Right now, I am trying to get the button to redirect onClick to the first path.
Below I have added the relevant areas of my code and hope someone can assist.
Display component:
import Sidebar from '../../Components/SideNav/Sidebar'
import GenButton from '../../Components/Buttons/GenButton'
export default function Sales({ authorized }) {
let history = useHistory();
const handleRoute = () =>{
history.push("/")
}
if (!authorized) {
return <Redirect to='/' />
}
return (
<div>
<Sidebar />
<div className='content'>
<h2>Sales Page</h2>
<GenButton
value="Home"
onClick={handleRoute}
/>
</div>
</div>
)
}
GenButton code:
import React from 'react'
import './GenButton.css';
const GenButton = ({value, onClick}) => {
return <button className='btn' onClick={() => onClick}>
{value}
</button>
}
export default GenButton
I need to understand more about why this isn't working, as multiple components I need to create will have between 2-4 buttons that need to route towards other pages or events.
Thank you for your assistance.
Because onClick is a function inside your Gen Button component you need to call it as a function.
import React from 'react'
import './GenButton.css';
const GenButton = ({value, onClick = () => {}}) => {
return <button className='btn' onClick={() => onClick()}>
{value}
</button>
or just
import React from 'react'
import './GenButton.css';
const GenButton = ({value, onClick = () => {}}) => {
return <button className='btn' onClick={onClick}>
{value}
</button>
I added a default value to onClick too incase there isn't one on a particular button.

How to show a button after 10 seconds using react hooks

I am very new to React hooks can someone please help me how to show a button after 10 seconds I tried a little but I don't know how to implement it.
This is my code
import React, { useState, useEffect } from 'react';
import './App.css';
const App = () => {
const [show, setShow] = useState(false)
useEffect(() => {
});
return (
<div className='container'>
<div className='row'>
<div className='col-12'>
<div className='main'>
<button className='btn btn-primary'>Click here</button>
</div>
</div>
</div>
</div>
)
}
export default App
```
Try this:
useEffect(() => {
setTimeout(() => setShow(true), 10000);
}, []); []: this is important as it will run this effect only once on component load
and use it like:
{show && <button className='btn btn-primary'>Click here</button>} // this will show the button when show state will be true

Hide react bootstrap toast when user clicks outside

I have a toast notification created using react-bootstrap. I need to dismiss it if the user clicks anywhere outside the toast. Someone knows how can I do that?
This is the toast I'm using:
//what am I importing
import { Toast } from 'react-bootstrap';
//how am I using it
<Toast
onClose={() => props.OnClose(false)}
data-testid="toast"
show={props.Show}
delay={10000}
autohide
>
<Toast.Header closeButton={false}>
<div>
{props.Icon}
</div>
<div>
Title
</div>
<div
onClick={() => props.OnClose(false)}
>
<ToastClose />
</div>
</Toast.Header>
<Toast.Body>
Body text
</Toast.Body>
</Toast>
What you can use is React hook useRef. Using useRef hook we can access the DOM elements easily and in this case we are accessing the Toast component and tracking clicks that happen outside of the component and firing the setShow(false) to update the state and dismiss the Toast component. This code will dismiss the Toast if the user clicks anywhere outside of it:
import { Toast } from "react-bootstrap";
import { useState, useRef, useEffect } from "react";
export default function App() {
const [show, setShow] = useState(true);
function useOutsideAlerter(ref) {
useEffect(() => {
/**
* Close toast if clicked on outside of element
*/
function handleClickOutside(event) {
if (ref.current && !ref.current.contains(event.target)) {
setShow(false);
}
}
// Bind the event listener
document.addEventListener("mousedown", handleClickOutside);
return () => {
// Unbind the event listener on clean up
document.removeEventListener("mousedown", handleClickOutside);
};
}, [ref]);
}
const wrapperRef = useRef(null);
useOutsideAlerter(wrapperRef);
return (
<div className="App">
<Toast
onClose={() => setShow(false)}
data-testid="toast"
show={show}
delay={10000}
autohide
ref={wrapperRef}
>
<Toast.Header closeButton={false}>
<div>Toast title</div>
<div onClick={() => setShow(false)}>Close button</div>
</Toast.Header>
<Toast.Body>Body text</Toast.Body>
</Toast>
</div>
);
}
Original post with more info: link

Change the state of one item in a map in react

I have a page that displays images fetched with an api call using the map method. Now what I want to do is to toggle the like or unlike state of FavoriteIcon by clicking it.
When I try to implement that using the useState hook, if I click on the FavoriteIcon in a single image, all the images are set to the same state. I want only the clicked image to change its state.
import React, { useState, useEffect } from "react";
import FavoriteIcon from "#material-ui/icons/Favorite";
import FavoriteBorderIcon from "#material-ui/icons/FavoriteBorder";
// services
import medias from "../../services/mediaServices";
function Favorites({ fetchUrl, catagory }) {
const classes = useStyles();
const [images, setImages] = useState([{}]);
const [favIcon, setFavIcon] = useState();
useEffect(() => {
async function getData() {
const request = await medias.getMedias(fetchUrl);
setImages(request.data.message);
}
getData();
}, [fetchUrl, enqueueSnackbar, closeSnackbar]);
return (
<div className={classes.favorites}>
<div className={classes.favorites__items}>
{images &&
images.map(image => {
return (
<div
elevation={3}
className={classes.favorites__posters}
style={
{
// border: "2px solid black"
}
}
>
<img
key={image.id}
src={image.thumbnailUrl}
alt={image.title}
/>
{favIcon ? (
<FavoriteIcon onClick={() => setFavIcon(false)} />
) : (
<FavoriteBorderIcon onClick={() => setFavIcon(true)} />
)}
</div>
);
})}
</div>
</div>
);
}
export default Favorites;
Without breaking it into more components, your favIcon state needs to contain the state of all your icons. It should be initialized with something like
images.map(image => {id: image.id, state: false})
and then you could change your onClick to just update the relevant part of state
setFavIcon([...favIcon].map(x => {if(x.id === image.id) {x.state = !x.state}; return x}))

Resources