React Swiper.js Deleting slide at index rendered from array - reactjs

I can't seem to figure out how to delete a slide at the current index which is rendered from an array using Swiper.js. Code Sandbox link here.
I have tried:
function deleteSlide() {
swiper.current.swiper.removeSlide(currentIndex);
var temp = slides;
temp.splice(currentIndex, 1);
setSlides(temp);
}
But the problem is that it does a double delete when the state updates. If I only run removeSlide, it removes the slide but my array is not updated. If I do anything else, my array updates but the slides don't update until I do a swipe. Adding slides to the end of the array so far works fine.
Without explicitly calling removeSlide, I tried the following:
useEffect(() => {
swiper.current.swiper.update();
}, [slides]);
But I'm not quite sure why this isn't actually updating the Swiper instance, according to the docs that's what I believe it should be doing. I recognize that this isn't being called when the state updates in deleteSlide either, and I'm not entirely sure why.
The ideal behavior is that the current view stays put, and when the current slide is deleted, the slide at index + 1 slides into position. If you delete the very last slide, then you slide to the 2nd last slide and the last slide gets deleted. I can take care of this as long as I can actually update the Swiper instance correctly.
How should I go about this? Appreciate any help, thank you.

Managed to solve this:
function deleteSlide() {
var temp = [...slides];
temp.splice(currentIndex, 1);
setSlides(temp);
}
Just had to use the spread ... operator to copy the array correctly. Although it is worth noting that this resets the state for all the other rendered slides, I need to figure out how to fix that.
EDIT: Updated Code Sandbox. To prevent re-rendering of all other array elements, the key of each element must be unique across renders. Fixed by setting the key to be a count, which is strictly increasing.

Related

How to hold screen position when data added in react?

I have a list in react that added more rows with interval,
my problem is that when the list grown the screen going up.
I want to hold the position of screen how he was before I added rows to list.
That is my code:
setInterval(() => {
//this row save the position of the screen before the adding row
setPrevRef({scrollHeight:tabElement.current.scrollHeight,scrollTop:tabElement.current.scrollTop})
props.setFilteredPT (prev=> [newRow,...prev])
}, 10000);
and
useEffect(()=>{
tabElement.current.scrollTop =prevRef.scrollTop + (tabElement.current.scrollHeight - prevRef.scrollHeight )
},[props.filteredPT])
After I wrote this code the screen going up to a second and than comeback to the place.
here is how react does it with class component: https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#reading-dom-properties-before-an-update
but I want do it with function component.
Somebody can help?
I had found that my problem cause I used for map key the index, when I have changed it to uniq ID it worked good.

Performance issues if MapComponent state is updated

I am not sure if this is an issue of react-leaflet-markercluster, react-leaflet, leaflet, react, or my code.
I have a map with several thousand markers and I am using react-leaflet-markercluster for marker clustering. If I need to update a global state of MapComponent, there is 1-3 seconds delay when this change is reflected.
I created a codesandox with 5000 markers and you can see there 2 use cases with performance issues:
1.) MapComponent is inside react-reflex element, that allows resizing panel and propagates new dimensions (width, height) to MapComponent. If width and height are changed, mapRef.invalidateSize() is called to update map dimensions. Resizing is extremely slow.
2.) If user clicks on Marker, global state selected is updated. It is a list of clicked marker ids. Map calls fitBounds method to focus on clicked marker and also marker icon is changed. There is around 1 second delay.
In my project, if I need to change a MapComponent state, it takes 2-3 seconds in dev mode when changes are reflected and it is just a single rerender of MapComponent and its elements (markers).
I took a look at Chrome performance profile and it seems like most time is spent in internal React methods.
It is possible to fix this by preventing rerendering using memo, which is similar to shouldComponentUpdate, but it makes whole code base too complicated. preferCanvas option doesn't change anything. I am wondering what is a good way to fix these issues.
The main problem I identified in your code is that you re-render the whole set of marker components. If you memoize the generation of those, you achieve a good performance boost; instead of running the .map in JSX, you can store all the components in a const; this way, the .map won't run on every render.
from this
...
<MarkerClusterGroup>
{markers.map((marker, i) => {
...
to something like this
const markerComponents = React.useMemo(() => {
return markers.map((marker) => {
return (
<MarkerContainer .../>
);
});
}, [markers, onMarkerClick]);
return (
<>
<MarkerClusterGroup>{markerComponents}</MarkerClusterGroup>
</>
);
The second refactor I tried is changing the way you select a marker. Instead of determining the selected prop from the selected array for each marker, I put a selected field on every marker object and update it when selecting a marker. Also, I add the position to the onClickHandler args to avoid looking for that in the markers array.
There are some other tweaks I don't explain here so please check my codesandbox version.
https://codesandbox.io/s/dreamy-andras-tfl67?file=/src/App.js

How to hide or remove an element onClick in React?

I am trying to hide an element 'GorillaSurfIn' after I click on it.
But also it should fire the 'setShouldGorillaSurfOut' to 'true'. The second part works, but after I added this function:
function hideGorillaSurfIn() {
let element = document.getElementById('gorilla-surf-in');
ReactDOM.findDOMNode(element).style.display =
this.state.isClicked ? 'grid' : 'none';
}
After I click, the code falls apart.
Once I click, the element should be hidden/removed till the next time the App restarts.
Here is the Code Sandbox Link for further explanation.
I am open to any solutions, but also explanations please, as I am still fresh in learning React.
I have changed your code a bit to make it work. You can make further changes according to your need. A few things that I would like to add: -
You should avoid using findDOMNode (in most cases refs can solve your problem) as there are certain drawbacks associated with findDOMNode, such as the react's documentation states "findDOMNode cannot be used with functional components".
I've used refs (forward ref in this case) to make it work.
GorillaSurfIn was called twice, so there were two Gorilla gifs on the screen with same IDs. Not sure if that was the intended behaviour but each element should have unique ID.
Check out the code sandbox.

Scroll to props.location.hash using useEffect after component mounts

I am creating a glossary page where each term has it's own card that is not expanded by default. Each card uses the term as it's ID because they will all be unique. I want to support direct links to a specific term via URL hash.
So, if the URL is localhost:3000/#/glossary#Actor, the initial load will scroll to the term and 'open' the card by simulating the click on the element (click handler on that element opens the card).
It is working, but too frequently. Every time I enter text into the search bar or cause a render in any way, it re-scrolls and does the card open animation again. I just want to initially scroll to the term if there is a hash, then ignore it unless it changes.
It only works if I don't include a dep array. If I include a dep array with props.location.hash, the el returns as null and nothing happens. I'm aware that the restarting of the effect is happening because of no dep array, but I don't know why it doesn't work when I add it.
useEffect(() => {
//looks for hash in URL; gets element associated with the hash
let el = document.getElementById(decodeURI(props.location.hash.slice(1)));
console.log(el)
//if element exists, get coords and offset for header before scrolling
if (el) {
const yCoordinate = el.getBoundingClientRect().top + window.pageYOffset;
const yOffset = -80;
window.scrollTo({
top: yCoordinate + yOffset,
behavior: 'smooth'
});
//opens the card only if not already open
if (!el.classList.contains('openTermCard')) {
el.click();
}
}
})
useEffect may not be the correct method. I might not even need a hook for this, idk. I just want this initial scroll event to happen on page load and not again.
It's a bit hard to figure out a solution without a working sandbox.
From your description, it seems to me your component is rendering more than once on the first load. If you put [] as dependency array, the effect only runs once, when the element is not defined yet (hence why you get null) and that's why it doesn't work.
One way to go about it is to think what else you can do in terms of designing your app. You mention that every time your component updates it focuses the card again, but that's kind of what you expect when you're using useEffect. Is it possible to re-structure the component so that the parent (the one with this side effect) doesn't re-render as often?
If you can provide a code sandbox I'm sure I (and probably others) will be able to help you further.

ReactTable setState on columns is not changing layout

first I should mention that I'm very new to react so this might be something silly..
I've struggled to get ReactTable's column to be sortable with the help of mattlockyer's gist
What I don't understand is I call setState on columns and then the render call is triggered correctly but it doesn't update the layout.
The render function "columns" contains an updated object with the correct column order, however render does not change the column order nor does sorting columns content.
This is the code in question,
this.setState({
columns: newColLayout,
}, () => {
this.mountEvents();
});
It will trigger a render, but the columns stay the same.
Ive put together a codepen with my current work and with comments about the code in question.
Any help would be greatly appreciated.
OK so i think i figured out whats going on.
The issue is splice modifies the state without triggering a change event, then when the event is fired from setStat, react doesn't see the change made to the column layout as it matches the current value.
The way i get around this is by cloning the column array using slice.
// Clone the current column state.
const columnClone = this.state.columns.slice(0);
// Remove item from array and stick it in a new position.
columnClone.splice(i, 0, columnClone.splice(this.draggedCol, 1)[0]);
Here is the updated code...
Still might be improved by someone who has more experience with react.

Resources