In React 17 jquery issue? - javascript

I'm using React. I need jquery functionality. I solved this problem with npm install jquery. But is such a use correct? I have never seen a user before. Will I encounter a problem?

It's quite strange to mix a framework like React with jQuery. Not completely unheard of, but it's almost never the right way to do things.
Here, you can achieve the same effect more properly by
using addEventListener instead of jQuery
using useEffect to add the listener once (and to remove it once the component unmounts)
Setting state which changes the nav's class via JSX instead of doing it with DOM manipulation
const Header = () => {
const [shrink, setShrink] = useState(false);
useEffect(() => {
const handler = () => {
setShrink(window.scrollY > 50);
}
document.addEventListener('scroll', handler);
return () => document.removeEventListener('scroll', handler);
}, []);
const navClass = (shrink ? 'shrink ' : '') + 'navbar navbar-expand-lg'; // etc
return (
<header>
<nav className={navClass}>
...

Related

React - How to change number of array items mapped based on screen width?

I have an array that I am mapping over and rendering a component for each item. I want to reduce the number of items mapped from 5 to 4 at screen widths below 1194px, and to increase it back to 5 items above this width.
This is what I have tried so far but it causes the app to freeze if I mess around with the window size, and also initially renders 5 regardless of screen width.
This seems complex, is there an easier way to do this?
P.S - I have only included the relevant parts of the component.
// Section for loading specific number of cards
// set number of anime to map via state so it can be change in media queries
const [numberToMap, setNumberToMap] = useState(5);
//dynamically load the anime cards so that they can use the 'number' variable
const mapAnime = (number) => (
<div className='anime-list-wrapper'>
{animeData?.data?.slice(0, (number)).map((anime) => (
<AnimeCard anime={anime} key={anime.id} />
))}
</div> )
window.matchMedia('(min-width: 1194px)').addEventListener('change', () => setNumberToMap(4));
window.matchMedia('(max-width: 1195px)').addEventListener('change', () => setNumberToMap(5));
return (
<div className='container'>
<h3 className='category-title'>{categoryTitle}</h3>
{!isFetching
? <div className="wrapper">
{mapAnime(numberToMap)}
</div>
You're not cleaning up when the component unmounts. Basically you have a lot of event handlers for the change event whenever the components unmounts.
you should reverse addEventListener by using removeEventListerner("change", handler), but the requires to keep a reference to the handler.
Practically speaking, you need to set the event listeners in useEffect and return a clean up function.
const minHandler = () => setNumberToMap(4);
const maxHandler = () => setNumberToMap(5);
useEffect(() => {
const minMedia = window.matchMedia("(min-width: 1194px)");
minMedia.addEventListener("change", minHandler);
const maxMedia = window.matchMedia("(max-width: 1195px)");
maxMedia.addEventListener("change", maxHandler);
return () => {
minMedia.removeEventListener("change", minHandler);
maxMedia.removeEventListener("change", maxHandler);
};
}, []);
Or better still, you use a custom hook for matchMedia, which will take care of the cleanup.
You're updating the react state on each screen width change. So when you were messing with it in the dev tools, you got freezes because of the sheer amount of state changes. The screen won't change often, but generally it is a good idea to avoid frequent unoptimized state updates. Anyways, your listeners are listening to the same thing, so one is unnecessary. Also, update the state only when events match.
Do this and you're good:
useEffect(() => {
const mediaQuery = window.matchMedia('(max-width: 1194px)');
const handler = (event: MediaQueryListEvent) => {
if (event.matches) {
setNumberToMap(4);
} else {
setNumberToMap(5);
}
};
mediaQuery.addEventListener('change', handler);
return () => mediaQuery.removeEventListener('change', handler);
}, []);

What is the proper way to smoothly scale an element to nothing and then back to its original size

This is how I do it in React:
const animate_boxes = () => {
inner_ref.current.style.transform = "scale(0)";
setTimeout(() => {
if (inner_ref && inner_ref.current) {
inner_ref.current.style.transform = "scale(1)";
}
}, 200);
};
For some reason, it is not dependable. the setTimeout may not always get run. Still haven't figured out why yet. But If there is an alternative way to do it, then please write a solution.
thanks
May I suggest the react-spring library?
Use framer motion library with scale property passed in as an array of values
eg :
import {motion} from 'framer-motion'
const Component = () => {
return (
<motion.div
initial={{scale: 1}}
animate={{scale: [0.1, 1]}}
>
hi
</motion.div>
)
}
Refer this docs for more examples - Docs

Is it possible to remove and readd React eventListener within the handler function?

I am working with something like fullpage.js with React, and I need to remove the eventListener while the transition is ongoing.
Is it possible?
React code
function App() {
const wheelHandler = (event) => {
// I need to remove wheelHandler here
setTimeout(() => {
// I need to readd wheelHandler here
}, 1000); // Assume that the transition ends after 1000ms
};
return (
<div className="App" onWheel={wheelHandler} />
);
}
Vanilla JS equivalent
const wheelHandler = (event) => {
window.removeEventListener(wheelHandler);
setTimeout(() => {
window.addEventListener(wheelHandler);
}, 1000);
};
window.addEventListener(wheelHandler);
P.S. I tried the Vanilla JS solution on React but the event handler got triggered multiple times on one wheel scroll. Therefore I got no choice but React's SyntheticEvent.
With the way you're hooking it up, you can't without using a piece of state that tells you whether to hook up the handler and re-rendering, which is probably overkill.
Instead, I'd set a flag (perhaps on an object via a ref) telling the handler to ignore calls during the time you want calls ignored.
Something long these lines:
function App() {
const {current: scrolling} = useRef({flag: false});
const wheelHandler = (event) => {
// I need to remove wheelHandler here
if (scrolling.flag) {
// Bail out
return;
}
scrolling.flag = true;
// ...other logic if any...
setTimeout(() => {
// I need to readd wheelHandler here
scrolling.flag = false;
}, 1000); // Assume that the transition ends after 1000ms
};
return (
<div className="App" onWheel={wheelHandler} />
);
}
Or you can also do it like this, you don't need an extra object (I tend to prefer to use a single ref that holds all of my non-state instance data, but you don't have to):
function App() {
const scrolling = useRef(false);
const wheelHandler = (event) => {
// I need to remove wheelHandler here
if (scrolling.current) {
// Bail out
return;
}
scrolling.current = true;
// ...other logic if any...
setTimeout(() => {
// I need to readd wheelHandler here
scrolling.current = false;
}, 1000); // Assume that the transition ends after 1000ms
};
return (
<div className="App" onWheel={wheelHandler} />
);
}
As they say in the useRef documentation, refs are useful for non-state instance information:
However, useRef() is useful for more than the ref attribute. It’s handy for keeping any mutable value around similar to how you’d use instance fields in classes.

Dynamically creating refs without strings

EDIT: We're using React 16.2.0, which is relevant to the question (see this answer).
So far as I can tell, this is the accepted way to create a ref (at least for our version of react):
<div ref={(r) => { this.theRef = r; }}>Hello!</div>
And then it can be used something like this:
componentDidMount() {
if (this.theRef) {
window.addEventListener('scroll', this.handleScroll);
}
}
This works fine. However, if I want to create a dynamically named ref, say as part of a loop, how do I go about naming the ref?
Put in now obsolete terms, I would like something along these lines:
<div ref="{refName}">Hello!</div>
Thanks!
Try just:
<div ref={refName}>Hello!</div>
For a map you need a key, so maybe you could just use that key to map to an object? Like so:
this.myRefs = {}
doSomethingToRef = (key) => {
this.myRefs[key].doSomething()
}
return (
myValues.map(value => (
<div key={value.key} ref = {r => {this.myRefs[value.key] = r}}>{...}</div>
))
)
Use ref like this:
Define the refName inside the class constructor:
this.refName = React.createRef()
Assign the ref in your element:
<div ref={this.refName} id="ref-name">Hello!</div>
To access the refName, use current:
this.refName.current
Example:
if (this.refName.current.id == 'ref-name') {
window.addEventListener('scroll', this.handleScroll);
}
Update
As per your comment, to use ref in older version, you may use just like this:
<div ref={(el) => this.refName = el} id="ref-name">Hello!</div>
{/* notice double quote is not used */}
To access:
this.refs.refName
Example:
if (this.refs.refName.id == 'ref-name') {
window.addEventListener('scroll', this.handleScroll);
}
To do it in more better way, you may use callback pattern.
[short-id][1] might be a good candidate!
It has methods like:
ids.store('foo'); // "8dbd46"
ids.fetch('8dbd46'); // 'foo'
ids.fetch('8dbd46'); // undefined
ids.configure(Object conf)
$ npm install short-id
RefsCmp.js
var shortid = require('shortid');
const RefsList = (newrefs) = > (
{newrefs.map(item => (
<div ref={shortid.generate()}>Hello!</div>
))}
)
export default RefsList;

Vue - watch for changes on this.$el.clientWidth

Is it possible to watch for changes on components element clientWidth (this.$el.clientWidth)?
Something like:
this.$watch(
() => {
return this.$el.clientWidth;
},
(newWidth, oldWidth) => {
console.log(newWidth);
}
)
I am investigating the same problem.
I think the solution dependent on the HTML lifecycle is effective in some cases.
I am pleased if my solution at the present stage will serve you.
mousemove (e) {
this.clientWidth = this.$el.clientWidth
}

Categories

Resources