swiper.updateSize()/swiper.update() not working swiperjs React - reactjs

I am looking to be able to update the size of .swiper-wrapper when there is a DOM event on the current slide. The method swiper.updateSize()/swiper.update() is firing after the DOM event but it is returning undefined. This is odd because when I console.log(swiper) (the instance of swiper) it returns the swiper object instance as expected. I have added the prop autoHeight to so according to the documentation, the height of the .swiper-wrapper should increase/decrease depending on the height of the current slide after the swiper.updateSize()/swiper.update() method is called
Is there something I am doing wrong here?
import React, { useState } from 'react';
function ParentComponent(props) {
let [swiper, setSwiper] = useState(null)
return(
<Swiper
onSwiper={(swiper) => {setSwiper(swiper)}}
autoHeight
>
<ChildComponent
title={"title"}
text={"text"}
swiper={swiper}
/>
</Swiper>
)
}
function ChildComponent(props) {
let [active, setActive] = useState(false)
let swiper = props.swiper
// toggle active class
const handleClick = () => {
if (active) {
setActive(false)
}
else {
setActive(true)
// returns swiper instance
console.log(swiper)
// returns undefined
swiper.updateSize();
}
}
return (
<div className={"infos" + (active ? ' active' : '')}>
<div onClick={handleClick}>
<h4>{props.title}<span></span></h4>
</div>
<div className="text">
<p>{props.text}</p>
</div>
</div>
)
}
export default ChildComponent

ok managed to fix this by setting a timeout and waiting until after the transition had finished (or roughly close to it). The issue seemed to be that the max-height transition hadn't finished by the time the update() method was called on swiper. I also used a different method called .updateAutoHeight() which worked
my code now looks like this:
import React, { useState } from 'react';
function ParentComponent(props) {
let [swiper, setSwiper] = useState(null)
return(
<Swiper
onSwiper={(swiper) => {setSwiper(swiper)}}
autoHeight
>
<ChildComponent
title={"title"}
text={"text"}
swiper={swiper}
/>
</Swiper>
)
}
function ChildComponent(props) {
let [active, setActive] = useState(false)
let swiper = props.swiper
// toggle active class
const handleClick = () => {
if (active) {
setActive(false)
setTimeout(() => {
swiper.updateAutoHeight()
}, 200)
}
else {
setActive(true)
setTimeout(() => {
swiper.updateAutoHeight()
}, 250)
}
}
return (
<div className={"infos" + (active ? ' active' : '')}>
<div onClick={handleClick}>
<h4>{props.title}<span></span></h4>
</div>
<div className="text">
<p>{props.text}</p>
</div>
</div>
)
}
export default ChildComponent
I would suggest changing the timeout ms depending on how long the transition takes to finish. The higher the height of the max-height transition, the longer it will take and therefore the more delay you will need to use on the setTimeout ms for it to be picked up by the JS

All methods of swiper need to be called in setTimeout function

Related

How set state for one individual component React when having multiple components

I have this Navbar with 3 tabs, and I managed to build a hook that sets a different style when clicked (changing its class); however, i don't know how target a state directly to just one tab. When clicked, all of then change their states. how I use the "this" in react in a case like this
const [isActive, setIsActive] = useState(false);
const handleClick = () => {
setIsActive(current => !current);
};
const setName = () => {
return (isActive ? 'true' : 'false');
}
return (
<NavStyled>
<div className="navbar-div">
<nav className="nav">
<p className={setName()} onClick={handleClick} >Basic</p>
<p className={setName()} onClick={handleClick} >Social</p>
<p className={setName()} onClick={handleClick} >Certificates</p>
</nav>
</div>
</NavStyled>
);
};
export default Navbar; ```
Navbar is a function component, not a hook.
You need to store either the currentTabIndex or currentTabName in the state.
var [currentTabName, setCurrentTabName] = useState('Basic');
handleClick=(evt)=> {
setCurrentName(evt.target.textContent);
};
['Basic','Social','Certificates'].map((tabName, i)=> {
let clazz = (tabName == currentTabName)?'active':'';
return <p key={tabName} className={clazz} onClick={handleClick} >{tabName}</p>
});

React check if element is visible on viewport

I am trying to identify on scroll if the div is visible on viewport. I am shring the code below:
<div id="parent">
data.map(item => {
<div id={item.id}>data.title</div>
}
<div>
Now I want to get the list of divs inside of #parent which are visible on viewport on scroll.
You can install the 'react-intersection-observer' module from the npm to do the trick. Read more from here. They have the 'inView' hook that could solve your problem.
First import the hook.
import { useInView } from "react-intersection-observer";
const [ref, inView] = useInView({
/* Optional options */
triggerOnce: true,
rootMargin: '0px 0px',
})
here, we can ref our element by
<div ref={ref}></div>
and the inView returns true when it is visible on the viewport and false when it is not.
React-Intersection-Observer using with Multiple Divs
Install package npm i react-intersection-observer
Create new file MultipleObserver.js
// MultipleObserver.js
import { useInView } from 'react-intersection-observer';
const MultipleObserver = ( {children} ) => {
const { ref, inView } = useInView({ triggerOnce: true });
return (
<div ref={ref}>
{ inView ? <span>{children}</span> : 'Loading...' }
</div>
)
}
export default MultipleObserver;
Now you can use multiple divs in the same components see example MyComponent.js
// MyComponent.js
import React from 'react';
import MultipleObserver from "MultipleObserver.js";
const MyComponent = ( props ) => {
return (
<div className="main">
<h2>Page Content</h2>
<MultipleObserver>
<img src="large-image.png" />
</MultipleObserver>
<MultipleObserver>
<iframe src="domain.com/large-video.mp4"></iframe>
</MultipleObserver>
<p>Another content</p>
</div>
}
export default MyComponent
This helped me:
<div id="parent">
data.map(item => {
<div id={`id_${item.id}`}>data.title</div>
}
<div>
and
const displayIds = (target) => {
console.log(target.replace("id_", ""));
}
const myScrollHandler = debounce(() => {
data.map(
(item: any) => {
const target =
document.querySelector(`#id_${item.id}`);
const top = target?.getBoundingClientRect().top || 0;
top >= 0 && top <= window.innerHeight ? displayIds(target);
}
);
}, 500);
useEffect(() => {
document.addEventListener("scroll", myScrollHandler);
return () => document.removeEventListener("scroll", myScrollHandler);
}, [data]);
Now for every scroll I have list of id's associated with div's which is visible on viewport.

what's the difference between 'onClick' and 'onMouseEnter' in React?

I am trying to add side toggle menu box in main page which is positioning on right side of document when the button is clicked.
I made a function for toggling action(you can check the function below) with some of react hooks and added by using onClick() method. I clicked the btn to check if it works, but it doesn't. I changed onClick() method to onMouseEnter() and it worked. I added callback(onClick(()=>{function()})) but it still doesn't work.
I think toggling function doesn't have any problem(because it worked properly when it's on onMouseEnter). Some mechanisms of them makes the difference. I checked the docs of javascript but it was not helpful.
I wish somebody provide me a demonstration of this.
Here is my codes.
import React, { useState } from "react";
import "../css/side-toggle.css";
import hbgBtn from "../image/hamburgerBtn.png";
const SideBar = ({ wid = 380, children }) => {
const [isOpen, setIsOpen] = useState(false);
const toggleMenu = () => {
setIsOpen((isOpen) => !isOpen);
console.log('1')
};
const closeMenu = () => {
setIsOpen((isOpen) => !isOpen);
}
return (
<div className="container" wid={wid}>
<img onMouseEnter={(e) => toggleMenu(e)}
className={!isOpen ? "show-btn" : "hide"}
src={hbgBtn}
alt=""
/>
<div onMouseEnter={closeMenu} className={isOpen? "dimmer" : 'hide'}>{children}</div>
<div className={isOpen ? "side-column" : "hide"}></div>
</div>
);
};
export default SideBar;
(and i will appreciate when you understand my wierd English. I'm ESL)
There is a possiblity that you may have made some error while styling. I have tried my best to replicate your code and it works for me fine.
import React, { useState } from 'react';
import './style.css';
const SideBar = ({ wid = 380, children }) => {
const [isOpen, setIsOpen] = useState(false);
const toggleMenu = () => {
setIsOpen((isOpen) => !isOpen);
console.log('1');
};
const closeMenu = () => {
setIsOpen((isOpen) => !isOpen);
};
return (
<div className="container">
<div onClick={(e) => toggleMenu(e)}>
<img
className={!isOpen ? 'show-btn' : 'hide'}
src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Hamburger_icon.svg/1024px-Hamburger_icon.svg.png"
alt=""
/>
</div>
<div onMouseEnter={closeMenu} className={isOpen ? 'dimmer' : 'hide'}>
{children}
</div>
<div className={isOpen ? 'side-column' : 'hide'}></div>
</div>
);
};
export default SideBar;
Here is the stackblitz code link
https://stackblitz.com/edit/react-xtwxzc?file=src/App.js

How to update state for device width using Hooks in react

I am working on a React project, according to my scenario, a have button in my project and I have written two functions to change background color. First function will call if device width is less than or equal to 320px. Second function will call if device width is === 768px. but here the problem is when my device width is 320px when I click the button at that time the background color is changing to red here the problem comes now when I go to 768px screen then initially my button background color has to be in blue color, but it is showing red. to show button background color blue I have to update state for device size.
So someone please help me to achieve this.
This is my code
This is App.js
import React, { useState } from 'react';
import './App.css';
const App = () => {
const [backGroundColor, setBackGroundColor] = useState(null)
const [deviceSize, changeDeviceSize] = useState(window.innerWidth);
const changeBackGroundColorForMobile = () => {
if(deviceSize <= 320) {
setBackGroundColor({
backgroundColor: 'red'
})
}
}
const changeBackGroundColorForTab = () => {
if(deviceSize === 768) {
setBackGroundColor({
backgroundColor: 'green'
})
}
}
return (
<div className='container'>
<div className='row'>
<div className='col-12'>
<div className='first'>
<button onClick={() => {changeBackGroundColorForMobile(); changeBackGroundColorForTab() }} style={backGroundColor} className='btn btn-primary'>Click here</button>
</div>
</div>
</div>
</div>
)
}
export default App
If you have any questions please let me know thank you.
You're always running two functions. Don’t need that.
You’re updating the deviceSize only on the initial render. You have to update that in orientation change also.
Set the default colour always to blue.
import React, { useEffect, useState } from "react";
import "./App.css";
const App = () => {
const [backGroundColor, setBackGroundColor] = useState({
backgroundColor: "blue"
}); // Initialize bgColor with "blue"
const [deviceSize, changeDeviceSize] = useState(window.innerWidth);
useEffect(() => {
const resizeW = () => changeDeviceSize(window.innerWidth);
window.addEventListener("resize", resizeW); // Update the width on resize
return () => window.removeEventListener("resize", resizeW);
});
const changeBgColor = () => {
let bgColor = "blue";
if (deviceSize === 768) {
bgColor = "green";
} else if (deviceSize <= 320) {
bgColor = "red";
}
setBackGroundColor({
backgroundColor: bgColor
});
}; // Update the bgColor by considering the deviceSize
return (
<div className="container">
<div className="row">
<div className="col-12">
<div className="first">
<button
onClick={changeBgColor}
style={backGroundColor}
className="btn btn-primary"
>
Click here
</button>
</div>
</div>
</div>
</div>
);
};
export default App;
I would follow the previous advice to get the width and if you have lots of child components that rely on the width then I would suggest using the useContext hook so you don't have to keep passing the window data as a prop.
You can use useWindowSize() hook to get window width. And whenever width changes you can change background color by calling the functions in useEffect()
import { useState, useEffect } from "react";
// Usage
function App() {
const [backGroundColor, setBackGroundColor] = useState(null)
const { width } = useWindowSize();
useEffect(()=>{
if(width <= 320) {
changeBackGroundColorForMobile();
}
if(width === 768) {
changeBackGroundColorForTab()
}
}, [width])
const changeBackGroundColorForMobile = () => {
setBackGroundColor({
backgroundColor: 'red'
})
}
const changeBackGroundColorForTab = () => {
setBackGroundColor({
backgroundColor: 'green'
})
}
return (
<div className='container'>
<div className='row'>
<div className='col-12'>
<div className='first'>
<button style={backGroundColor} className='btn btn-primary'>Click here</button>
</div>
</div>
</div>
</div>
)
}
// Hook
function useWindowSize() {
// Initialize state with undefined width/height so server and client renders match
// Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/
const [windowSize, setWindowSize] = useState({
width: undefined,
height: undefined,
});
useEffect(() => {
// Handler to call on window resize
function handleResize() {
// Set window width/height to state
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
// Add event listener
window.addEventListener("resize", handleResize);
// Call handler right away so state gets updated with initial window size
handleResize();
// Remove event listener on cleanup
return () => window.removeEventListener("resize", handleResize);
}, []); // Empty array ensures that effect is only run on mount
return windowSize;
}
You can use useEffect hook to add an event listener to window resize.
export default function App() {
const [bgClassName, setBgClassName] = useState("btn-primary");
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
function updateWidth() {
setWidth(window.innerWidth);
if(window.innerWidth === 768){
setBgClassName('btn-primary')
}
}
window.addEventListener("resize", updateWidth);
return () => window.removeEventListener("resize", updateWidth);
}, []);
const changeColor = () => {
if (window.innerWidth < 320) {
setBgClassName("btn-danger");
} else if (window.innerWidth === 768) {
setBgClassName("btn-success");
}
};
console.log(width);
return (
<div className="container">
<div className="row">
<div className="col-12">
<div className="first">
<button
onClick={() => changeColor()}
className={`btn ${bgClassName}`}
>
Click here
</button>
</div>
</div>
</div>
</div>
);
}

React check if the event is bubbled/captured without using Ref

this is the solution using refs
import React, { useState, useRef } from "react";
function Bar() {
const ref = useRef();
return (
<div
ref={ref}
onClick={(ev) => {
if (ev.target == ref.current) console.log("Target hit");
}}
>
Click me, maybe <div>test</div>
</div>
);
}
In the above solution, target is not hit when we click on test. Can we
get the same output without using refs
Use the currentTarget property of the event, which will always refer to the element it is registered on, as opposed to target which is the element that triggered it:
function Bar() {
return (
<div
onClick={(ev) => {
if (ev.target === ev.currentTarget) console.log("Target hit");
}}
>
Click me, maybe <div>test</div>
</div>
);
}

Resources