Hi all I have following code
const App = () => {
const mediaRef = useRef(null);
const navRef = useRef(null);
const [direction, setDirection] = useState(null);
const onChange = (currentSlide) => {
if (direction === "next") {
mediaRef.current.goTo(currentSlide + 1, false);
} else {
mediaRef.current.goTo(currentSlide - 1, false);
}
};
const handleNext = () => {
setDirection("next");
navRef.current.next();
};
const handlePrev = () => {
setDirection("prev");
navRef.current.prev();
};
const imageList = [ some images array ];
return (
<>
<>
<Carousel
asNavFor={navRef.current}
ref={mediaRef}
>
{imageList?.map((el, id) => (
<ImageWrapper key={id}>
<img src={el} alt={"name"} />
</ImageWrapper>
))}
</Carousel>
{imageList?.length > 1 && (
<>
<Button onClick={handlePrev}>
<LeftOutlined />
</Button>
<Button onClick={handleNext}>
<RightOutlined />
</Button>
</>
)}
</>
{imageList?.length > 1 && (
<Carousel
slidesToShow={3}
centerMode={true}
asNavFor={mediaRef.current}
ref={(carousel) => (navRef.current = carousel)}
beforeChange={onChange}
>
{imageList?.map((el, id) => (
<>
<divkey={id}>
<img src={el} alt={"name"} />
</div>
</>
))}
</Carousel>
)}
<>
<Button onClick={handlePrev}>
<LeftOutlined />
</Button>
<Button onClick={handleNext}>
<RightOutlined />
</Button>
</>
</>
);
};
I am doing following, I am taking antd carousel adding another carousel for thumbnails and putting custom arrows, clicking on next and on previouse buttons it automatically change main image and thumbnail.
Now I am trying to put onClick event on thumbnails, I mean Thumbnails should be clickable and by click, the large image and thumbnail should be changed. How can I achieve that ?
You can add a click handler on the thumbnail with the clicked id as parameter
<ThumbnailWrapper key={id}>
<img
src={el}
alt={"name"}
onClick={() => thumbnailClicked(id)}
/>
</ThumbnailWrapper>
then do something with the id in the defined function:
const thumbnailClicked = (id) => {
console.log("thumbnail clicked:",id);
//then e.g. set parent the same as the thumbnail.
mediaRef.current.goTo(id, false);
};
Related
I have multiple images which I can select from a carousell. These images are rendered from a map function like this:
{imagenesProductos.map((img) => {
if (img) {
console.log(img);
return (
<SwiperSlide className="">
<Image onClick={e => clickedImage(e)} className="product" src={img} width={100} height={100} alt='productImage' />
</SwiperSlide>
)
}
})}
When I click the image I have a method that just console logs the src of the image.
const clickedImage = (e) => {
console.log(e.target.src)
}
What I want to do is to add a class to the selected image and remove it when I select another one. I am using styled components.
I hope you can help me! Thanks in advance.
You can use a state to track clicked images. if the current image in the map is a selected image then you can add a class active or anything you like. I am using index as key as list of image wouldn’t change often I suppose.
Following is the sample code for it.
const [curImageIndex, setCurImageIndex] = useState(null);
const clickedImage = (index) => {
setCurImageIndex(index);
};
return (
<>
{magenesProductos.map((img, index) => {
if (img) {
console.log(img);
return (
<SwiperSlide
className={`product ${curImageIndex === index? 'active' : ''}`}
key={index}
>
<Image
onClick={() => clickedImage(index)}
className="product"
src={img}
width={100}
height={100}
alt="productImage"
/>
</SwiperSlide>
);
}
})}
</>
);
You should have a state that holder an identifier of this specific selected image. For example:
const [selectedPath,setSelectedPath]=useState('');
Than, in the map you can add a condition with this state, and use the className where you need.
{imagenesProductos.map((img) => {
if (img) {
console.log(img);
return (
<SwiperSlide className="">
<Image onClick={clickedImage} className=`product ${img===selectedPath?"selected":""}` src={img} width={100} height={100} alt='productImage' />
</SwiperSlide>
)
}
})}
And the clickImage function should be:
const clickedImage = (e) => {
console.log(e.target.src);
setSelectedPath(e.target.src);
}
Use a single state to hold the currently selected image. Filter imagenesProductos before mapping. Compare the currently mapped img to the stored selectedImage state to conditionally add the other class to the image.
const [selectedImage, setSelectedImage] = React.useState(null);
...
const clickedImage = (e) => {
const { src } = e.target;
console.log(src);
setSelectedImage(src);
}
...
{imagenesProductos
.filter(img => img)
.map((img) => {
console.log(img);
return (
<SwiperSlide className="">
<Image
onClick={clickedImage}
className={["product", selectedImage === img ? "newClass" : ""].join(" ")}
src={img}
width={100}
height={100}
alt='productImage'
/>
</SwiperSlide>
);
}
})}
So I have 4 panels inside this Collapse from ant design and all 4 of them share the same states.
Here's the code for the Collapse:
const CustomCollapse = (props) => {
const [disabled, setDisabled] = useState(true);
const [followed, setFollowed] = useState(false);
const [opened, setOpen] = useState(false);
let [key, setKey] = useState([props.keys]);
useEffect(() => {
setFollowed(props.isFollowed)
}, [props.isFollowed])
const handlePanel= () => setOpen(prev => !prev);
const handlePanelClose = () => props.setShow(prev => !prev);
const combineFunc = () =>{
handlePanel();
handlePanelClose();
}
return (
<StyledCollapse accordian activeKey={props.show ? key : []} onChange={combineFunc}>
<AntCollapse.Panel
{...props}
header={props.header}
showArrow={false}
bordered={false}
key={props.keys}
extra={
<span>
<span style={{float: 'right'}}>
{followed ? <img src={tickIcon} alt="" style={{height:'1.2em', marginLRight:'10px', width:'auto', objectFit:'contain'}} /> : ""}
</span>
<div className={styles.extraContainer}>
{
!opened && !followed && <div id={styles.themeBox}><p>+10</p></div> // show this box
}
{
opened && !followed && <img src={arrowDownIcon} alt="" style={{height:'1.2em', marginLRight:'10px', width:'auto', objectFit:'contain'}} /> // show this icon
}
</div>
</span>
}
>
{props.children}
</AntCollapse.Panel>
</StyledCollapse>
);
};
Here's the code in father component which uses Custom Collapse which is imported as AntCollapse:
<AntCollapse isFollowed={followed} show={show} keys="1" setShow={setShow} id={styles.telegramHeader1} header="Follow XXX on Telegram">
<Row type='flex' align='middle' justify='center'>
<Button href={links} target="_blank" style={buttonStyle1} onClick={() => setClicked(false)}>
<Icon type="link" style={{ color: '#fff' }} theme="outlined" />
Subscribe
</Button>
</Row>
<span className={styles.greyLine}> </span>
<Row type='flex' align='middle' justify='center'>
<Button onClick={() => {handleFollow(); handleShow(); }} style={buttonStyle2} disabled={clicked}>Continue</Button>
</Row>
</AntCollapse>
I have 4 similar AntCollapse and I only show 1 of them here cause all 4 of them use followed and show. And whenever I expand the panel and click continue, all 4 of them are set to followed and show. How do I change the states seperately?
I am trying to make history by pushing on button click
onclick function of the li is not working
as u can see in the code <SuggestionsList/> is in the last return statement its funtions are rendered in const SuggestionsList = (props) => { . The onclick funtion is comimg inside the const SuggestionsList funtion, this is making the onclick funtion not working
i created the exact working in codesand and its working there without any problem i dont get why its not working in my local
enter link description here
function finddoctor(e) {
console.log(e);
history.push(`/detiled/${e} `);
}
const onChange = (event) => {
const value = event.target.value;
setInputValue(value);
setShowResults(false);
const filteredSuggestions = suggestions.filter(
(suggestion) =>
suggestion.firstname
.toString()
.toLowerCase()
.includes(value.toLowerCase()) ||
suggestion.id.toString().toLowerCase().includes(value.toLowerCase())
);
setFilteredSuggestions(filteredSuggestions);
setDisplaySuggestions(true);
};
const onSelectSuggestion = (index) => {
setSelectedSuggestion(index);
setInputValue(filteredSuggestions[index]);
setFilteredSuggestions([]);
setDisplaySuggestions(false);
};
const SuggestionsList = (props) => {
const {
suggestions,
inputValue,
onSelectSuggestion,
displaySuggestions,
selectedSuggestion,
} = props;
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list" style={styles.ulstyle}>
{suggestions.map((suggestion, index) => {
const isSelected = selectedSuggestion === index;
const classname = `suggestion ${isSelected ? "selected" : ""}`;
return (
<>
<li
style={styles.listyle}
onClick={finddoctor(suggestion.id)}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</>
);
})}
</ul>
);
} else {
return <div>No suggestions available...</div>;
}
}
return <></>;
};
useEffect(() => {
axios
.get("admin-panel/all-doctors-list/")
.then((res) => {
const data = res.data;
setShowSerch(data);
});
}, []);
return (
<>
<div className="note-container" style={styles.card}>
<div style={styles.inner}>
<p style={{ textAlign: "left" }}>Search Doctors</p>
<form className="search-form" style={{}}>
{showResults ? (
<FontAwesomeIcon
style={{ marginRight: "-23px" }}
icon={faSearch}
/>
) : null}
<input
onChange={onChange}
value={inputValue}
style={styles.input}
type="Search"
/>
<SuggestionsList
inputValue={inputValue}
selectedSuggestion={selectedSuggestion}
onSelectSuggestion={onSelectSuggestion}
displaySuggestions={displaySuggestions}
suggestions={filteredSuggestions}
/>
</form>
</div>
</div>
</>
);
};
Instead of onClick={finddoctor(suggestion.id)} (Here just function invocation is happening and expected to have the callback method)
should be
onClick={() => finddoctor(suggestion.id)}
I have implemented a simple drag and drop code here below using react-beautiful-dnd. The frontend alone works perfectly fine, however, when I tried to connect this with the backend the items in the droppable context are unable to be dragged. That is, I am unable to drag the items within the column and also between columns. Further, I am unable to figure out how to pass the index of the elements in the mongoDb database.
The code I used is here below
projectsDashboard.js
function ProjectsDashboard() {
const handleDragEnd = ({destination, source}) => {
if (!destination) {
return
}
if (destination.index === source.index && destination.droppableId === source.droppableId) {
return
}
// Creating a copy of item before removing it from state
const itemCopy = {...state[source.droppableId].items[source.index]}
setState(prev => {
prev = {...prev}
// Remove from previous items array
prev[source.droppableId].items.splice(source.index, 1)
// Adding to new items array location
prev[destination.droppableId].items.splice(destination.index, 0, itemCopy)
return prev
})
}
const dispatch = useDispatch();
useEffect(() => {
dispatch(getStages());
},[dispatch]);
const { stage } = useSelector(state => state.stage);
var formattedArray = stage.map(item => Object.keys(item).map(i => item[i]));
console.log(formattedArray)
return (
<DragDropContext onDragEnd={handleDragEnd}>
{_.map(state, (data, key) => {
return(
<div key={key} className={"column"}>
{console.log(key , "KEY")}
<ProjectWrapper className="border">
<h3 className="title">{data.title}</h3>
</ProjectWrapper>
<Droppable droppableId={key}>
{(provided, snapshot) => {
return(
<div>
<div
ref={provided.innerRef}
{...provided.droppableProps}
className={"droppable-col"}
>
<hr className="line" style={{opacity: 10 }}></hr>
{stage.map(stages=>{
if(stages.status == key)
return(
<Draggable key={stages._id}
//index={index}
draggableId={stages._id} className="drag">
{(provided, snapshot) => {
console.log(snapshot)
return(
<div
className={`item ${snapshot.isDragging && "dragging"}`}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
{/* card name */}
<button className="stageDetails" style={{padding: "0",border: "none", background: "none"}}>
{stages.stageName}
</button>
<DeleteOutlinedIcon className="delete" />
</div>
)
}}
</Draggable>
)
})}
{provided.placeholder}
</div>
</div>
)
}}
</Droppable>
</div>
)
})}
</DragDropContext>
</div>
);
};
export default ProjectsDashboard;
See that loop loading two components. I would like to display only <Image /> by default, but when I click this element, I want it to turn into <YouTube /> (only the one I press, the others are still <Image />). I can do this on a class component, but I want to use a hook
export const MusicVideos = () => {
const [selectedVideo, setVideo] = useState(0);
return (
<Wrapper>
{videos.map(video => (
<div key={video.id}>
<Image src={video.image} hover={video.hover} alt="thumbnail" />
<YouTube link={video.link} />
</div>
))}
</Wrapper/>
);
};
you can bind onClick for your image and setVideo to video.id and compare with video.id to render image or video.
export const MusicVideos = () => {
const [selectedVideo, setVideo] = useState(0);
return (
<Wrapper>
{videos.map(video => (
{selectedVideo !== video.id ?
<Image onClick={() => setVideo(video.id) src={video.image} hover={video.hover} alt="thumbnail" /> :
<YouTube link={video.link} />
))}
</Wrapper/>
);
};
Create a component like this and pass it to the loop;
const YouTubeToggle = (video) => {
const [selectedVideo, setVideo] = useState(0);
return (
<div key={video.id}>
{selectedVideo == 0 &&
<Image src={video.image} onClick={() => setVideo(!selectedVideo)} hover={video.hover} alt="thumbnail" />
}
{selectedVideo != 0 &&
<YouTube link={video.link} />
}
</div>
);
}
export const MusicVideos = () => {
const [selectedVideo, setVideo] = useState(0);
return (
<Wrapper>
{videos.map(video => (
<YouTubeToggle video={video} />
))}
</Wrapper/>
);
};