Append slides dynamically in Swiper slider in React - reactjs

I am creating a new application in React. I want to use the swiper slider for the banners.
The banners will be fetched from APIs. How do I append slides to the swiper once it reaches the last slide. I am using onReachEnd function to trigger the api call. Not sure how to append slides dynamically.
export const HeroSlider = () => {
const [pageNumber, setCurrentPageNumber ] = useState(0)
const { homeBannerData, isLoading, error } = useBannerData(pageNumber)
var dataHomeBanner = []
if (homeBannerData && homeBannerData.result) {
dataHomeBanner = dataHomeBanner.concat(homeBannerData.result.data)
}
function fetchBannerData() {
setCurrentPageNumber(pageNumber+1)
if (homeBannerData && homeBannerData.result) {
dataHomeBanner = dataHomeBanner.concat(homeBannerData.result.data)
}
}
return (
/* Hero Banner Start */
<section className="hero-slider">
<Swiper
slidesPerView={1}
watchSlidesVisibility = {true}
navigation
onReachEnd={() => fetchBannerData()}
autoHeight = {true}
>
{dataHomeBanner && dataHomeBanner.map((slide, index)=>{
return (
<SwiperSlide key={index}>
<img src={slide.image alt={slide.title} className="w-100"/>
<Container>
<div className="hero-slider-body d-flex align-items-end align-items-md-center">
<Row>
<Col md={12}>
<div className="hero-slider-caption">
<h1>{slide.title}</h1>
<p>{slide.description}</p>
</div>
</Col>
</Row>
</div>
</Container>
</SwiperSlide>
)
})}
</Swiper>
</section>
/* Hero Banner End */
)
}
useBannerData is returning the banner data.
Right now this code is adding the new slides and replacing the old ones once it fetches data with a new page number. I want it to append at the end of the last slides so that when a user reaches the last slide and clicks on next slide it should fetch data and append it at the end of last slide.
TIA!

Related

How to force update current slide in Swiper?

I have some images and slider with these images. I'm trying update current active slide with onClick event: (clicked image must be active slide)
In following code i've tried to change first-indexed image, but it doesn't work with Swiper, so I need force rerender my Swiper-slider, for the reason it started from first.
So the main goal is to make the clicked image the current slide
const [prev, setPrev] = useState()
const [ImageArray, setImageArray] = useState([item1, item2, item3])
const [slidesArray, setSlidesArray] = useState([item1, item2, item3])
const swiper = useSwiper()
const clickHandler = (image: any) => {
if (prev !== image) { //if clicked not the same
let index = slidesArray.indexOf(image) // find index of image in image array
let temp = [...slidesArray]
temp.splice(index, 1) //delete selected image
temp.unshift(image) // set first
setSlidesArray([...temp])
setPrev(image)
swiper.reInit() //not working
}
}
<div className={classes['Gallery__container']}>
{ImageArray.map((image, index) => (
<img src={image} alt="" key={index} width={200} height={200} onClick={() => clickHandler(image)}/>
))}
</div>
<Swiper
spaceBetween={50}
slidesPerView={'auto'}
loop
zoom={true}
>
{slidesArray.map((image,index) => (
<SwiperSlide key={index}>
<img src={image} alt="" width={200} height={200}/>
</SwiperSlide>
))}
</Swiper>
P.S. my code is working and first slide is changing, but I need to change the current

Image not getting rendered in react application

Unable to render images in react, In browser console, image names are displayed correctly but on UI it is not displayed. Also, src attribute is missing from img tag after inspecting element. Please assist. Here is Sample Script
function importAll(r) {
let images = {};
r.keys().map((item, index) => { images[item.replace('./', '')] = r(item); });
return images;
}
const get_images = importAll(require.context('../../images', true, /\.(png|jpe?g|svg)$/));
const images = Object.entries(get_images).map(module => module[1].default);
return (
<div className="product">
<div>
<Row>
{products.map(product =>
<Col span={6} key={product.productId}>
<div >
<div>{console.log(product.productImageName)}
{<Image src={images[product.productImageName]} width={200}
height={200} />}
</div>
</div>
</Col>
)
}
</Row>
</div>
</div>
)
You don't need to import images. if images are part of your repository then you can use the relative path from the src directory. e.g assuming the images in under src
const path = `images/${[product.productImageName]}`;
<Image src={path} />
You should try and check if profile exist. Like this:
{profile && profile.map((...
//Or
{profile?.map((...

React useEffect not updating when switching between pages

I have two states (userCustomers and loans) which are depending on a state coming from a custom hook (customers).
const [customers] = useLoanCustomers(state.contents.date)
const [userCustomers, setUserCustomers] = useState<DwCustomerHeadline[]>([])
const [loans, setLoans] = useState<DwLoanContract[]>([])
So I filter data depending on the output of the useLoanCustomers and have an empty array as initial state.
When I refresh the page, the useEffect works fine and sets the states of "userCustomers" and "loans"
But when I switch between pages/routes, the useEffect does not work and keeps the initial state [].
e.g this component is on 'http://localhost:3000/user/overview'.
if I go to another route like 'http://localhost:3000/clients' and then back to '/user/overview', the states "userCustomers" and "loans" are empty. So the useEffect does not set those states.
"useLoanCustomers" is loading as expected and holds all the data.
when I console log inside the useEffect, I can see that useEffect is running properly depending on the dependency change.
But it is not setting the other two states. As said, only when I refresh the page.
Has anyone a clue what's wrong, because I've already tried so much but can't figure it out.
This is my entire component
export const Overview: React.FC<OverviewProps> = ({ user }) => {
const { keycloak } = useKeycloak()
const { state } = useContext(DashboardContext)
const [customers] = useLoanCustomers(state.contents.date)
const [userCustomers, setUserCustomers] = useState<DwCustomerHeadline[]>([])
const [loans, setLoans] = useState<DwLoanContract[]>([])
useEffect(() => {
const filteredCustomers = customers.filter((customer) => customer.stafferEmail === user?.email)
setUserCustomers(filteredCustomers)
Promise.all(filteredCustomers.map((customer) => getCustomerLoans(keycloak, customer.counterpartyId))).then((res) =>
setLoans(res.flat())
)
}, [customers])
return (
<>
<div className="grid gap-4 p-2 md:grid-cols-3 sm:grid-cols-1">
<div className="col-span-2 shadow-sm">
<KeyMetrics />
</div>
<div className="shadow-sm">
<NextReview customers={userCustomers} />
</div>
</div>
<div className="grid gap-4 p-2 md:grid-cols-3 sm:grid-cols-1 grid-rows-2">
<div className="md:h-80 col-span-2 shadow-sm">
<ErpOverview customers={userCustomers} />
</div>
<div className="row-span-2 shadow-sm" style={{ height: '34rem' }}>
<Outflow loans={loans} endDate={state.contents.date} />
</div>
<div className="h-52 col-span-2 shadow-sm">
<LoanArrears customers={userCustomers} />
</div>
</div>
</>
)
}

Get location path from use Location hook inside a column renderer from react-bootstrap-table2

Story
I'm creating 2 pages (Summary and Cycles pages) using react.js.
On the Summary page, there is a column named CN that every item links to the Cycles page.
Summary page has a path /route/summary/location=abc and Cycles page has a path /route/cycle/location=abc/deviceId=4410
For example, if I click the value from CN column in the first row of the table inside the Summary page, I will be redirected to the Cycles page with the path /route/cycle/location=abc/deviceId=4410.
In the Summary page, I use https://github.com/react-bootstrap-table/react-bootstrap-table2 for the table component and I use a columnRenderer function inside columns.js to render a custom item inside the table like this one:
Question
How can I put the pathname (example "abc") to a Link component inside cnColumnRenderer function in columns.js?
Ideal Condition I wanted:
Summary page with the path: /route/summary/location=abc
Cycles page with the path: /route/cycle/location=abc/deviceId=4410
Actual Condition:
Error because of invalid hook call while rendering the Summary page
My Code:
table code inside Summary page (inside Summary.js):
hint: focus on columns variable from './columns' and its implementation
import React from "react"
import { useLocation } from "react-router-dom"
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory, {
PaginationProvider,
PaginationListStandalone
} from 'react-bootstrap-table2-paginator';
import ToolkitProvider, { Search } from 'react-bootstrap-table2-toolkit';
import columns from './columns'
const Summary = () => {
const location = useLocation()
const locationName = location['pathname'].replace('/route/summary/location=', '')
// console.log('Summary, location:', locationName)
// example: location = "/route/summary/location=abc" and locationName = "abc"
// ...some code here...
return (
<React.Fragment>
<div className="container-fluid ppa-route-summary-root">
<Row>
<Col className="col-12">
{/* ...some code here... */}
{/* TABLE CARD */}
<Card>
<CardBody>
<PaginationProvider
pagination={paginationFactory(paginationOptions)}
keyField='id'
columns={columns}
data={tableData}
>
{
({ paginationProps, paginationTableProps }) => (
<ToolkitProvider
keyField='id'
columns={columns}
data={tableData}
search
>
{
toolkitProps => (
<React.Fragment>
{/* ...some code here... */}
{/* TABLE ITSELF */}
<Row>
<Col xl="12">
<div className="table-responsive">
{
isTableLoading ?
<ReactLoading
type={'spin'}
color={'crimson'}
height={'5rem'}
width={'5rem'}
className='table-loading'
/> :
<BootstrapTable
keyField={"id"}
responsive
bordered={false}
striped={false}
// defaultSorted={defaultSorted}
// selectRow={selectRow}
classes={
"table align-middle table-nowrap"
}
headerWrapperClasses={"thead-light"}
{...toolkitProps.baseProps}
{...paginationTableProps}
/>
}
</div>
</Col>
</Row>
{/* ...some code here... */}
</React.Fragment>
)
}
</ToolkitProvider>
)
}
</PaginationProvider>
</CardBody>
</Card>
</Col>
</Row>
</div>
</React.Fragment>
)
}
export default Summary
columns.js:
import React from 'react'
import { Link, useLocation } from 'react-router-dom'
// IMAGES
import IconLocation from '../../../images/icons/location.svg'
const cnColumnRenderer = (cell, row, rowIndex, formatExtraData) => {
// console.log('columns, cnColumnRenderer:', cell, row, rowIndex, formatExtraData)
const deviceVersion = cell.split('-')[1] // example: deviceVersion = "4410"
const location = useLocation()
// console.log('Summary columns, location:', location['pathname'])
// here is the pathname I wanted: "/route/cycle/location=abc" so I can take the "location" path value as below:
const locationName = location['pathname'].replace('/route/summary/location=', '') // the result is locationName = "abc"
// then put the locationName inside the Link component
return(
<div className='route-summary-cn'>
<img src={IconLocation} alt='' className='icon-location'/>
{/* below is the pathname I wanted: "/route/cycle/location=abc/deviceId=4410" */}
<Link to={`/route/summary/location=${locationName}/deviceId=${row['deviceId']}`}>
{deviceVersion}
</Link>
</div>
)
}
const columns = [
{
dataField: 'deviceName',
text: 'CN',
formatter: cnColumnRenderer,
sort: true
},
{
dataField: 'convertedTotalCycle',
text: 'Cycle',
sort: true,
},
// ...some code here...
]
export default columns
Note: let me know if the question is confusing. I will try to update it.
React hooks are only valid in React functional components, not in any callbacks, loops, conditional blocks. If you need the location data in the callback it needs to be passed in.
From what I can tell it seems you need to move the columns.js code into the main component so the location values can be closed over in scope.
const Summary = () => {
const location = useLocation();
const locationName = location['pathname'].replace('/route/summary/location=', '')
// example: location = "/route/summary/location=abc" and locationName = "abc"
// ...some code here...
const cnColumnRenderer = (cell, row, rowIndex, formatExtraData) => {
// console.log('columns, cnColumnRenderer:', cell, row, rowIndex, formatExtraData)
const deviceVersion = cell.split('-')[1] // example: deviceVersion = "4410"
// then put the locationName inside the Link component
return(
<div className='route-summary-cn'>
<img src={IconLocation} alt='' className='icon-location'/>
{/* below is the pathname I wanted: "/route/cycle/location=abc/deviceId=4410" */}
<Link to={`/route/summary/location=${locationName}/deviceId=${row['deviceId']}`}>
{deviceVersion}
</Link>
</div>
);
};
const columns = [
{
dataField: 'deviceName',
text: 'CN',
formatter: cnColumnRenderer,
sort: true
},
{
dataField: 'convertedTotalCycle',
text: 'Cycle',
sort: true,
},
// ...some code here...
];
return (
...
);

state is not updating when the component re renders?

I'm making a Nextjs flashcard app. I'm passing a deck structure like this:
const deck = {
title: 'React 101',
flashcards: [flashcardOne, flashcardTwo],
};
as props to the Deck component. This component shows the first card in flashcards and a "next" button to increment the index and showing the next card in flashcards.
The Card component is very simple and shows the front and the back of the card depending of the state front.
This is what I got so far and it's working but if I click "next" when the card is showing the answer (flashcard.back), the next card is going to appear with the answer. And I'm not sure why, isn't the Card component re rendering when I click "next"? And if the component re renders, front is going to be set to true?
export default function Deck({ deck }) {
const [cardIndex, setCardIndex] = useState(0);
const { title, flashcards } = deck;
return (
<div className={styles.container}>
<main className={styles.main}>
<h1 className={styles.title}>{title}</h1>
{cardIndex < flashcards.length ? (
<>
<div className={styles.grid}>
<Card flashcard={flashcards[cardIndex]} />
</div>
<button onClick={() => setCardIndex((cardIndex) => cardIndex + 1)}>
Next
</button>
</>
) : (
<>
<div>End</div>
<button>
<Link href='/'>
<a>Go to Home</a>
</Link>
</button>
<button onClick={() => setCardIndex(0)}>Play again</button>
</>
)}
</main>
</div>
);
}
export function Card({ flashcard }) {
const [front, setFront] = useState(true);
return (
<>
{front ? (
<div
className={`${globalStyles.card} ${styles.card}`}
onClick={() => setFront(false)}
>
<p className={styles.front}>{flashcard.front}</p>
</div>
) : (
<div
className={`${globalStyles.card} ${styles.card}`}
onClick={() => setFront(true)}
>
<p className={styles.back}>{flashcard.back}</p>
</div>
)}
</>
);
}
When state changes, the card will re-render, but it will not re-mount. So, existing state will not be reset.
Call setFront(true) when the flashcard prop has changed:
const [front, setFront] = useState(true);
useLayoutEffect(() => {
setFront(true);
}, [flashcard]);
I'm using useLayoutEffect instead of useEffect to ensure front gets set ASAP, rather than after a paint cycle (which could cause flickering).
You can also significantly slim down the Card JSX:
export function Card({ flashcard }) {
const [front, setFront] = useState(true);
const face = front ? 'front' : 'back';
return (
<div
className={`${globalStyles.card} ${styles.card}`}
onClick={() => setFront(!front)}
>
<p className={styles[face]}>{flashcard[face]}</p>
</div>
);
}
Okay, I guess I had the same issue. Since you're using functional components, and you're re-using the same component or in better words, you're not unmounting and remounting the component really, you're just changing the props, this happens. For this, you need to do useEffect() and then setFront(true).
Here's the code I used in my App.
useEffect(() => {
setFront(true);
}, [flashcard]);
This is what I have used in my Word.js file.

Resources