Bootstrap carousel: sets active item to first when data changes (Reactjs) - reactjs

My carousel with data.img is an array of link img
<div
id="carouselExampleControls"
className="codeinfo__carousel carousel slide"
data-bs-ride="carousel"
>
<div className="carousel-inner">
<div className="carousel-item active">
<img
src={data.img[0]}
className="codeinfo__img d-block w-100"
alt="..."
/>
</div>
{data.img.map(
(e, i) =>
i > 1 && (
<div className="carousel-item">
<img
src={e}
className="codeinfo__img d-block w-100"
alt="..."
/>
{i}
</div>
)
)}
</div>
<button
className="carousel-control-prev"
type="button"
data-bs-target="#carouselExampleControls"
data-bs-slide="prev"
>
<span className="carousel-control-prev-icon" aria-hidden="true" />
<span className="visually-hidden">Previous</span>
</button>
<button
className="carousel-control-next"
type="button"
data-bs-target="#carouselExampleControls"
data-bs-slide="next"
>
<span className="carousel-control-next-icon" aria-hidden="true" />
<span className="visually-hidden">Next</span>
</button>
</div>
When the data has changed, the active item does not return to the first item, but to the number of the previous active item. So if the length of the previous data.img is bigger than the length of the current data.img there will be nothing to display and the carousel will crash.(sorry for my english, I use google translate)
Like the example below. How to make class "active" return first item?
carousel1 > Change data > carousel2

I solved the problem myself. I removed all bootstrap actions and select active item using react code. I use useState to select active item, setInterval to autoPlay, useEffect to reset activity to 0. Here is my code:
const Carousel = (props) => {
const data = props.data;
const [nowActive, setNowActive] = useState(0);
const [onHover, setOnHover] = useState(false);
// autoplay (stop in hover)
useEffect(() => {
if (!onHover && data !== null) {
let i = nowActive;
const timer = setInterval(() => {
if (i >= data.img.length - 1) {
setNowActive(0);
i = 1;
} else {
setNowActive((prev) => prev + 1);
i = i + 1;
}
}, 3000);
return () => {
clearTimeout(timer);
};
}
}, [onHover, data]);
// change data >> return active = 0
useEffect(() => {
setNowActive(0);
}, [data]);
const carousel = () => {
return (
<div
id="carouselExampleControls"
className="codeinfo__carousel carousel slide"
data-bs-ride="carousel"
onMouseOver={() => setOnHover(true)}
onMouseLeave={() => setOnHover(false)}
>
<div className="carousel-inner">
{data.img.map((e, i) => (
<div className={"carousel-item " + (nowActive === i && "active")}>
<img src={e} className="codeinfo__img d-block w-100" alt="..." />
</div>
))}
</div>
<button
className="carousel-control-prev"
type="button"
onClick={() => {
nowActive === 0
? setNowActive(data.img.length - 1)
: setNowActive(nowActive - 1);
}}
>
<span className="carousel-control-prev-icon" aria-hidden="true" />
<span className="visually-hidden">Previous</span>
</button>
<button
className="carousel-control-next"
type="button"
onClick={() => {
nowActive === data.img.length - 1
? setNowActive(0)
: setNowActive(nowActive + 1);
}}
>
<span className="carousel-control-next-icon" aria-hidden="true" />
<span className="visually-hidden">Next</span>
</button>
</div>
);
};
return data !== null ?
<div>{carousel()}</div> :
<div>Null data</div>
Note: Although the problem has been solved, but this carousel does not have any Animation Effect when changing slides:

Carousel has a property called defaultActiveIndex, which is the initial value of activeIndex. You can use it to focus on the correct CarouselItem
Create a new State:
const [activeIndex, setActiveIndex] = useState(0);
Set the defaultActiveIndex to the newly created state:
Change the activeIndex whenever an item in the carousel is clicked:
onClick={(e) => { setActiveIndex(i); }}
Honestly they should just make this default behavior, not sure why we have to hack it.

Related

All my sidebar menu opens at once in ReactJS

I am trying to create a sidebar menu with dropdwon
For I single menu it works fine but if I have multiple dropdown
All opens at one when I click on the menu to open the submenu.
I am getting the data for the menu from a json array
const SidebarItems = ({ items }) => {
const [open, setOpen] = useState(false)
return (
<div>
{items && items.map((item, index) => {
return (
<div key={index}>
<div>
<li >
<div className="nav-items" onClick={() => setOpen(!open)}>
<span>
<i className="bi-badge-vr" />
{item.title}
</span>
<i className="bi-chevron-down" />
</div>
</li>
</div>
<div className={open ? 'sub-menu sub-menu-open' : "sub-menu"} >
{item.chidren && item.chidren.map((sub_menu, sub_index) => {
return (
<div key={sub_menu}>
<Link to="manager/staff/create">
<span>
<i className="bi-badge-vr" />
{sub_menu.title}
</span>
</Link>
</div>
)
})}
</div>
</div>
)
})}
</div>
)
}
This is because they all use the same state.
Move the item to its own component so I can keep its own state:
const Item = ({ item }) => {
const [open, setOpen] = useState(false);
return <Your item code here />;
};
And in SideBarItems:
items.map((item, index) => {
return <Item item={item} key={index} />;
});

render a placeholder image in place the images in those posts who don’t have an actual image location

this is the code i currently have - it shows posts from a subreddit - when i run the code in react, there are a few posts where images are not rendered since there is no data
for the thumbnail image - i want to render a thumbnail place holder image - i think i need to make another component and call it ? but i am not sure 
import React from "react";
import "./App.css";
function App() {
const [searchTerm, setSearchTerm] = React.useState("");
const [checked, setChecked] = React.useState(false);
const [posts, setPosts] = React.useState([]);
React.useEffect(() => {
fetch("https://www.reddit.com/r/popular.json")
.then((response) => response.json())
.then((result) => {
console.log(result.data.children);
setPosts(result.data.children);
})
.catch(() => {
console.log("You have an error.");
});
}, []);
const handleSearch = (event) => {
console.log(event.target.name);
console.log(event.target.checked);
console.log(event.target.value);
if (event.target.name === "searchFor") {
setSearchTerm(event.target.value);
}
if (event.target.name === "searchType") {
setChecked(event.target.checked);
}
};
//The searchedPosts variable below contains the posts after the filter is run on the subRData array
//TO start with it returns the posts whose title include the search term
//Modify it to return posts based on if the checkbox is checked or not
//If the box is checked then return the post based on a title search
//if the box is not checked then return the post based on if the number of comments on it are greater than the provided number in the search box.
const searchedPosts = posts.filter(function (post) {
if (checked) {
return post.data.title.toLowerCase().includes(searchTerm.toLowerCase());
}
if (post.data.num_comments >= searchTerm) {
return post;
}
});
//subreddit gets rendered right here
//maybe insert below the seperate component
return (
<div>
<h1> Reddit Data Display </h1>
<hr />
<Search term={searchTerm} doSearch={handleSearch} searchType={checked} />
<hr/>
<SubRPostDisplay posts={searchedPosts} />
<hr />
<Totals posts={searchedPosts} />
</div>
);
}
function Search(props) {
return (
<div>
<h4>Search based on Title/Comments </h4>
<input
name="searchFor"
type="text"
id="search"
value={props.term}
onChange={props.doSearch}
/>
<input
name="searchType"
className="ml-3 mr-3"
type="checkbox"
id="searchType"
value={props.searchType}
onChange={props.doSearch}
/>{" "}
(check for title search)
</div>
);
}
function SubRPostDisplay(props) {
console.log(props);
return props.posts.map(function (post) {
return (
<div id="post" data-key={post.data.created}>
<img src={post.data.thumbnail} alt={post.data.title} />
<span id="postLink">
<a href={post.data.url}>{post.data.title}</a>
</span>
<button
id="commentNum"
type="button"
className="btn btn-info btn-sm mr-2"
>
Comments{" "}
<span className="badge badge-light">{post.data.num_comments}</span>
</button>
<button id="upsNum" type="button" className="btn btn-info btn-sm mr-2">
Ups <span className="badge badge-light">{post.data.ups}</span>
</button>
<hr />
</div>
);
});
}
function Totals(props) {
let numcomments = 0;
let numups = 0;
props.posts.forEach(function (post) {
numcomments += post.data.num_comments;
numups += post.data.ups;
});
return (
<div>
<button type="button" className="btn btn-info mr-2">
Total number of Comments{" "}
<span className="badge badge-light">{numcomments}</span>
</button>
<button type="button" className="btn btn-info mr-2">
Total number of Ups <span className="badge badge-light">{numups}</span>
</button>
</div>
);
}
export default App;
It depends a bit on what you want exactly, but you should be able to just add a fallback image url to the image source. Something like this:
<img
src={post.data.thumbnail || 'https://link.to/fallback.png'}
alt={post.data.title}
/>

React prevent body from scrolling if popup is open

React prevent body from scrolling if popup is open.
How can i disable the background body from scrolling when the pop up div is open.
function FilterButton() {
let [isOpen, setIsOpen] = useState(false);
return (
<div className='filter-button'>
<div className="name-filter" onClick={() => setIsOpen(!isOpen)}>
<p>Filter</p>
<i class="fas fa-filter"></i>
</div>
{
isOpen ? <div className="background-blur">
<div className="filter-popup">
<p className='filter-by-name'>Filter By Type</p>
<hr />
<div className="filter-types">
<div className="filter">
<input type="checkbox" />
<p></p>
</div>
</div>
<div className="apply-cancel">
<button className="apply">Apply</button>
<button className="cancel" onClick={() => setIsOpen(!isOpen)}>Cancel</button>
</div>
</div>
</div>
: null
}
</div>
)
}
You can use the body-scroll-lock library to help you with that.
Here is how it could work on your component. I haven't tested it tho.
import { useState, useRef, useEffect } from 'react';
import {
disableBodyScroll,
enableBodyScroll,
clearAllBodyScrollLocks
} from 'body-scroll-lock';
function FilterButton() {
const [isOpen, setIsOpen] = useState(false);
const popupRef = useRef(null)
useEffect(() => {
if (isOpen) {
popupRef.current && disableBodyScroll(popupRef.current)
} else {
popupRef.current && enableBodyScroll(popupRef.current)
}
}, [isOpen])
return (
<div className="filter-button">
<div className="name-filter" onClick={() => setIsOpen(!isOpen)}>
<p>Filter</p>
<i class="fas fa-filter"></i>
</div>
{isOpen ? (
<div className="background-blur">
<div className="filter-popup" ref={popupRef}>
<p className="filter-by-name">Filter By Type</p>
<hr />
<div className="filter-types">
<div className="filter">
<input type="checkbox" />
<p></p>
</div>
</div>
<div className="apply-cancel">
<button className="apply">Apply</button>
<button className="cancel" onClick={() => setIsOpen(!isOpen)}>
Cancel
</button>
</div>
</div>
</div>
) : null}
</div>
);
}

How to stop sliding on last image of slider in carousel in bootstrap 5 on a reactjs project

Please tell me in js like how I can stop slide at last image in react js project
here is my Slider code and I want to remove the next and prev icons and also want to stop the slider when it reaches to IMG src={slider5}.
Please help me in find this answer
const Slider = () => {
const prevSlideHandler = () => {
console.log("clicked");
NotificationManager.error("You have rejected the image", "Rejected!", 1000);
};
const nextSlideHandler = () => {
NotificationManager.success("You have Liked the image", "Success", 1000);
};
return (
<section>
<div
id="carouselExampleIndicators"
class="carousel slide"
data-bs-ride="carousel"
data-wrap="false"
data-interval="false"
>
<div class="carousel-item">
<img src={slider4} class="d-block w-100" alt="..." />
</div>
<div class="carousel-item" id="lastSlider">
<img src={slider5} class="d-block w-100" alt="..." />
</div>
{/* <div class="carousel-item">
<img src={slider5} class="d-block w-100" alt="..." />
</div> */}
</div>
<button
class="carousel-control-prev"
type="button"
data-bs-target="#carouselExampleIndicators"
data-bs-slide="prev"
onClick={prevSlideHandler}
>
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button
class="carousel-control-next"
type="button"
data-bs-target="#carouselExampleIndicators"
data-bs-slide="next"
onClick={nextSlideHandler}
>
<span
class="carousel-control-next-icon myarrow"
aria-hidden="true"
></span>
<span class="visually-hidden">Next</span>
</button>
</div>
</section>
);
};
export default Slider;
You should have a variable in the state that keeps track of the current image the user is on in the carousel, let's call it "counter". It increments when you click next and decrements when you click previous.
const prevSlideHandler = () => {
console.log("clicked");
NotificationManager.error("You have rejected the image", "Rejected!", 1000);
this.setState((state, props) => ({
counter: state.counter += 1
}));
};
const nextSlideHandler = () => {
NotificationManager.success("You have Liked the image", "Success", 1000);
this.setState((state, props) => ({
counter: state.counter -= 1
}));
};
When the user reaches the end of the image, the counter should be equal to the number of images in the carousel. When this happens you should hide the next button with a ternary operator like this:
{
counter != [carousel_images].length?
<button
class="carousel-control-next"
type="button"
data-bs-target="#carouselExampleIndicators"
data-bs-slide="next"
onClick={nextSlideHandler}
>
<span
class="carousel-control-next-icon myarrow"
aria-hidden="true"
></span>
<span class="visually-hidden">Next</span>
</button>
}

how to call ".hidden.bs.dropdown" event using jquery inside react function component

I have a multi select dropdown. I am trying to call a function on dropdown close with all selected values but selected items are always empty.
const [selectedItems, setSelectedItems] = useState([]);
const dropdownRef = useRef(null);
useEffect(() => {
if(!isDropdownLoading)
$(dropdownRef.current).on("hidden.bs.dropdown", e => {
onDropdownClose(selectedItems);
//This function is getting called on dropdown close but selectedItems is always empty array.
});
}
}, [isDropdownLoading]);
const toggleItem=(event,selectedItem)=>{
//here logic to find selected items and update state
setSelectedItems(ArrayWithSelectedItems);
}
if (isDropdownLoading) {
return <p className="drop-down-onloading">Loading...</p>;
}
return (
<div ref={dropdownRef}>
<button
type="button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
title={titleText}
>
{name}
</button>
<div className="dropdown-menu">
{dropDownList.map(item => {
if (item.isVisible) {
return (
<button
className="dropdown-item"
onClick={e => toggleItem(e, item)}
key={item.id}
id={name}
>
<i
className={`${
item.isChecked
? "check-box"
: "empty-box"
}`}
></i>
{item.name}
</button>
);
}
})}
</div>
</div>
);
Why state is always empty array inside Jquery event. This is a function based react component.

Resources