Make SearchBar dissappear when resizing manipulating DOM in script - javascript

I have a header with the main navigationBar and a searchBar that appears only on small devices when clicking on an icon. For activating the searchBar I have a hook called useBoolean that returns a searchBarisShown, openSearchBar, closeSearchBar, and toggleSearchBar.
I have a wrapper for both navigationBar and searchBar and for animation purposes for the shadow I have another wrapper. It looks like this.
const Header = () => {
// Hooks
const [isSearchShown, openSearchBar, closeSearchBar, toggleSearch] = useBoolean()
const topMenuAnimation = useSpring({
transform: isSearchShown ? `translateY(0)` : `translateY(-100%)`,
})
const headerHeightAnimation = useSpring({
// 99px = 48px from search bar + 51px from header
height: isSearchShown ? '99px' : '51px',
})
if (window.innerWidth >= '990px') {
closeSearchBar()
}
return (
<HeaderMainContainer style={headerHeightAnimation}>
<HeaderBar>
{content}
</HeaderBar>
<SearchBar style={topMenuAnimation}>
<SearchFieldBar />
</SearchBar>
</HeaderMainContainer>
)
}
The thing is I want to close my searchBar with closeSearchBar when resizing over 990px because otherwise, I have my shadow way down below my navigation bar when this is closed. If I try to do this on styled-components with a media query it doesn't work because I am using styled-components and this only manipulates the virtual DOM and useSpring manipulates de real DOM. Do you have any suggestions on how to make this searchBar disappear?

Related

React, Expanding box inside wrapper causes wrapper to stretch rather than auto adjust

I've been trying to create a wrapper function so that i could load a sprite map into it and have an auto adjusting box that maintains the correct dimensions, rather than stretch the inital image that the sprites were made from. The problem occurs when the children of the wrapper are expanded, it causes the container box to expand but doesnt allow the wrapper to auto adjust its dimensions. This causes one of the borders, either top and bottom, or left and right to be larger/smaller than the other pair, which is not the desired effect.
The wrapper:
const AdaptBox = ({patternID, borderWidth, children}) =>{
const parentRef = useRef(null);
const [pixelHSize, setPixelHSize] = useState(borderWidth);
const [pixelWSize, setPixelWSize] = useState(borderWidth);
const imgs = SPRITE_PATTERNS[patternID];
useEffect ( () => {
console.log("use effect");
if(parentRef.current){
//useEffect hook to get the parents dimensions and then adjust ratio of wrapper
let parentHeight = parentRef.current.scrollHeight;
let parentWidth = parentRef.current.scrollWidth;
console.log(parentHeight);
console.log(parentWidth);
if(parentHeight<parentWidth){
console.log("wide scale");
setPixelHSize(((parentWidth/parentHeight)*borderWidth)/(parentWidth/parentHeight));
setPixelWSize(((parentHeight/parentWidth)*borderWidth));
}
else if(parentHeight>parentWidth){
console.log("tall scale");
setPixelHSize(((parentWidth/parentHeight)*borderWidth));
setPixelWSize(((parentHeight/parentWidth)*borderWidth)*(parentHeight/parentWidth));
}
else{
setPixelHSize(borderWidth);
setPixelWSize(borderWidth);
}
}
},[parentRef, borderWidth]);
return (
<Box img={imgs[4]} ref={parentRef}> //all styled comps
<BoxRow h={pixelHSize} w={100}>
<RowStuff.../>
</BoxRow>
<BoxRow h={100-(2*pixelHSize)} w={100}>
<VSide img={imgs[3]} h={100} w={pixelWSize}/>
<Back >
{children}
</Back>
<VSide img={imgs[5]} h={100} w={pixelWSize}/>
</BoxRow>
<BoxRow h={pixelHSize} w={100}>
<RowStuff.../>
</BoxRow>
</Box>
);
}
Implimentation of the wrapper:
...
<MainBodyContainer> //styled comp
<MainBody img={BGimg}> //styled comp
<AdaptBox patternID={0} borderWidth={1}> //Wrapper
<CoeffDisplay ></CoeffDisplay> //Child comp
</AdaptBox>
</MainBody>
</MainBodyContainer>
...
The "CoeffDisplay" contains a number of boxes that can be clicked on to expand.
Im open to another method of achieving this however i'm a fan of this wrapper style.
I've triple checked the simple maths but i think the issue is related to the use effect not being called on rerender.
Thanks for the help.

React intersection observer does not work when responsive

I'm using react-intersection-observer in order to make some animations when the component is in view.
It works cool, but, when i try to go to a mobile screen, like, 633px width and 400px height, it does not work, and i really don't know why.
Let me show you the code
So, what i'm doing here, is that, i want to know when the user is seeing 45% of the component, but, it gives false always, is it because this component is quite big, probably its height is 750px or even more and the height of the screen of the phone is only 400px ? i don't know... I'm just trying to understand why it doens't work
const DifferentWorks = () => {
const [scrollY, setScrollY] = useState(0);
const { inView, ref } = useInView({ threshold: 0.45 });
useEffect(() => {
scrollYProgress.onChange(n => setScrollY(n));
}, [inView, scrollYProgress, scrollY]);
console.log(inView)
return (
<div ref={ref}>
<DifferentWorksHero>
</DifferentWorksHero>
</div>
);
};
export default DifferentWorks;
And let me show you what is the output of the console.log(inView)
I've scrolled all the component, from bottom to top, and this is the output
Always false..
What could be generating that error ? why it doesn't work when i change the size of the screen ?, specifally when the width is 663px and the height is under 500px

How do I (correctly) check whether an element is within the viewport in React?

In a functional react component, I'm trying to check whether a call to action button (a different component) is within the viewport. If it's not, I want to display a fixed call to action button at the bottom of the viewport, which shows/hides, depending on whether the other button is visible.
I can do this using a combination of Javascript and react hooks, but although the code works in some components in my app, it doesn't work in others; I'm guessing due to react lifecycles.
I'm also aware that this is NOT the way I should be doing things in react, so would prefer to achieve the same result, but in a proper 'react way'.
I've been looking at using refs, but ideally wanted to avoid having to change my functional component to a class, as I'd like to use react hooks for the show/hide of the fixed cta. However, if this is a requirement in order to get the functionality I want, I could go for that.
Here's what I've got so far - basically, I want to replace document.querySelector with a react method:
useEffect(() => {
const CTA = document.querySelector('#CTANextSteps');
const ApplyStyle = () => (isInViewport(CTA) ? setVisible(false) : setVisible(true));
ApplyStyle();
window.addEventListener('scroll', ApplyStyle);
window.addEventListener('resize', ApplyStyle);
return function cleanup() {
window.removeEventListener('scroll', ApplyStyle);
window.removeEventListener('resize', ApplyStyle);
};
});
const isInViewport = (elem) => {
const bounding = elem.getBoundingClientRect();
return (
bounding.top >= 0 &&
bounding.left >= 0 &&
bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
bounding.right <= (window.innerWidth || document.documentElement.clientWidth)
);
};
As mentioned above, this function works in some areas of the app without issue, but doesn't in others; I get a Cannot read property 'getBoundingClientRect' of null error. I was surprised it worked at all, but rather than tinkering with it to try and get it working everywhere, I want to rewrite it properly.
As always, any assistance would be much appreciated. Thanks.
I was able to do it with the depedency react-visibility-sensor#5.1.1
I followed the tutorial in this link and it worked fine with me.
I don't know if this is the correct way to do it, but it works!
Here is the link https://www.digitalocean.com/community/tutorials/react-components-viewport-react-visibility-sensor
I'll put an example just in case the previous link ever goes out.
import React, { Component } from 'react';
import VisibilitySensor from 'react-visibility-sensor';
class VisibilitySensorImage extends Component {
state = {
visibility: false
}
render() {
return (
<VisibilitySensor
onChange={(isVisible) => {
this.setState({visibility: isVisible})
}}
>
<img
alt={this.props.alt}
src={this.props.src}
style={{
display: 'block',
maxWidth: '100%',
width: '100%',
height: 'auto',
opacity: this.state.visibility ? 1 : 0.25,
transition: 'opacity 500ms linear'
}}
/>
</VisibilitySensor>
);
}
}
export default VisibilitySensorImage;

onClick event on element on Mobile device hover state is still applied

I can't seem to find anyone with this same issue online with React, unless I've been searching for the wrong thing?
I have a button (anchor tag) that has an onClick event. When the onClick event is triggered it calls a loadMore() function that makes an API call and updates some state.
However on a mobile device (not mobile resolution on desktop!) when I click the button, the onClick event works and returns what is expected, however it applies a hover state on the button. For the button hover state I have a background color applied
The hover state will stick until I click away anywhere else on the screen. So the background color sticks until I click away.
Now, this isn't exactly desirable. Why is this happening, and how did I prevent it?
Here is my Button Component
const Button = props => {
const buttonDisabledClass = props.disabled ? "Button--disabled " : "";
const hiddenClass = props.hidden ? "Button--hidden " : "";
const modifierClass = props.modifier ? props.modifier : "";
return (
<>
<a
onClick={!props.disabled ? props.onClick : undefined}
className={
"Button " + buttonDisabledClass + hiddenClass + modifierClass
}
>
{props.children}
{props.buttonText ? (
<span
className={`Button-text ${
props.buttonMobileText ? "Button-desktopText" : ""
}`}
>
{props.buttonText}
</span>
) : null}
{props.buttonMobileText ? (
<span className="Button-mobileText">{props.buttonMobileText}</span>
) : null}
</a>
</>
);
};
Here is the parent component
The parent component imports the Button component and has the function that makes the API request (just have a simulated one here as an example)
function App() {
const [number, setNumber] = useState(0);
/*simulate http request*/
const ttl = 500;
const loadMore = () => {
const timeout = setTimeout(() => {
setNumber(number + 1);
}, ttl);
return () => {
clearTimeout(timeout);
};
};
return (
<div className="App">
{number}
<Button
key={"loadMoreBtn"}
modifier="Button--loadMore Button--Inline"
onClick={() => loadMore()}
>
Load More
</Button>
</div>
);
}
So, how can I make it so a click on a mobile device does not register hover but still have the hover working on a desktop device as it currently is?
I have a CODESANDBOX if you wish to test it out for yourself
Here is a link for you to test on your mobile device.
The button is orange by default, and grey on hover. On mobile, this is what happens when you click...
Any help would be greatly appreciated!
You could override the hover effect on mobile using a media query in your CSS:
#media only screen and (min-resolution: 117dpi) and (max-resolution: 119dpi), only screen and (min-resolution: 131dpi) and (max-resolution: 133dpi), only screen and (min-resolution: 145dpi) and (max-resolution: 154dpi), only screen and (min-resolution: 162dpi) and (max-resolution: 164dpi), only screen and (min-resolution: 169dpi) {
.Button:hover {
background-color: #ee4900 !important;
}
}
Or
#media (hover: none) {
.Button:hover {
background-color: #ee4900 !important;
}
}

How to get the value of scrollY while using overflow?

I've got the following React app where I'm using react-spring to animate between routes and animate different elements based on the current scroll position.
When I use overflow: scroll on the Home component I'm then unable to return anything from my handleScroll method (it just returns 0):
handleScroll(e) {
let windowScrollPosition = window.scrollY
this.setState({ windowScrollPosition: windowScrollPosition }, () => console.log(this.state.windowScrollPosition, 'this.state.windowScrollPosition'))
}
Is there a way around this?
I need to use overflow: scroll to solve this issue unfortunately.
Any help is much appreciated!
Well that would make sense. If you have set an element to scroll that is 100% height then the window would never scroll the element would.
So you need to get the scroll position from the element with elem.scrollTop
You can create a ref to the element in React
constructor(props) {
super(props);
this.scrollContainer = React.createRef();
}
return (
<div className="scroll-container" ref={this.scrollContainer}></div>
)
And then in your handle scroll method use:
handleScroll(e) {
let windowScrollPosition = this.scrollContainer.current.scrollTop | window.scrollY;
this.setState({ windowScrollPosition: windowScrollPosition }, () => console.log(this.state.windowScrollPosition, 'this.state.windowScrollPosition'))
}

Categories

Resources