Prevent DOM element re-render in virtualized tree component using react-window - reactjs

I want to re implement a tree component to improve its performance. I used the FixedSizeList from react-window. Its working relatively well. It can handle even 100,000 tree node.
My problem is, that I want to animate the little opening triangle of the tree node. The following css responsible for the animation:
.tree-branch::after {
content: '';
display: block;
width: 0;
height: 0;
margin-top: 1px;
margin-left: 23px;
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
border-left: 6px solid rgba(0, 0, 0, 0.7);
opacity: 0.7;
position: absolute;
top: 0;
left: -36px;
transform: rotate(90deg);
animation-duration: 0.3s;
}
.tree-item-closed::after {
transform: rotate(0deg);
}
The animation does not works. Because at each open and close all of the list element divs re-rendering. Then I tried to add itemKey property for the list to help React reusing the divs.
<List
className="List"
height={height}
itemCount={flattenedData.length}
itemSize={32}
width={width}
itemKey={index => flattenedData[index].id} // key defined
>
{Row}
</List>
It does not work either. The divs are not updated instead the whole divs are re-rendered. Is there a proper solution for this problem? How can I prevent the re-rendering?
Here is the whole example: https://codesandbox.io/s/a-quick-react-tree-component-based-on-react-window-tyxnm

To prevent react-window from re-rendering every item in the list, you need to:
Memoize your row-rendering function
Move that function outside the scope of the parent function
Use the itemData prop to communicate between the parent component and per-row render function.
This is the strategy used in the react-window example sandbox here: https://codesandbox.io/s/github/bvaughn/react-window/tree/master/website/sandboxes/memoized-list-items
Here's an example using your code:
https://codesandbox.io/s/a-quick-react-tree-component-based-on-react-window-8psp0
There may be a simpler way to do it using useMemo but I couldn't figure that out.
If you examine the DOM nodes using Chrome dev tools, you'll see that DOM nodes are no longer being re-created for the entire tree when one node is expanded. This removes the flicker that you'd see before (in Chrome, but not in Firefox) when selecting a new node.
But there's also something else that's preventing your rotation animations from working properly. In the CodeSandbox example I linked above, I added some animations on the border properties to show how animation of some CSS properties is working, but animation of the transform CSS properties is not working. I suspect this problem has nothing to do with react-window, so you'll probably want to debug it separately to figure out why your transforms aren't animating but other properties are animating OK.

Related

Can't absolute position an image in Swiper.js

I am trying to position an image inside a Swiper slider, and I need it to overflow the Swiper Container which has an overflow of hidden and display it on the bottom of another container. I am wondering is this possible to achieve?
see actual design
I have tried setting the Swiper Container to:
overflow: hidden;
and the .swiper-slide to:
.swiper-slide {
opacity: 0;
visibility: hidden;
transition: opacity 200ms ease-in-out, visibility 200ms ease-in-out;
}
so when the next slide come into play I trigger it back with:
.swiper-slide-active {
opacity: 1;
visibility: visible;
}
sadly this method makes the scroll bar appear on the bottom of the page...
see screenshot example here
also I made a Codesandbox to keep the things simple see codesandbox example

Material UI ghosting issue

I'm using a Switch component from Material UI to expand/collapse elements in a list. And when seeing the page in Safari on iOS, I'm experiencing a ghosting issue where the switches in the next elements often remain visible after expanding an element. This is especially bad when using transitions. But when scrolling the ghost switches disappear and everything looks fine.
Is this a Material UI issue or something else? Anything I can do as a workaround?
https://codesandbox.io/s/material-ui-switch-ghost-issue-2ie26?file=/src/App.js
The Switch ripple animation and Item animation are flighting for resources to do the animations and Safari seems like it is not able to optimise the animation process.
Therefore, you can add will-change: transform; in Item css to notify browser there will be a transform event and be ready:
const Item = styled.div`
max-height: ${({ $isSelected }) => ($isSelected ? "20rem" : "2.5rem")};
will-change: transform;
transition: max-height 0.1s ease-out;
overflow: hidden;
background: gray;
margin-bottom: 0.2rem;
`;
The definition of will-change:
https://developer.mozilla.org/en-US/docs/Web/CSS/will-change
Workable codesandbox:
https://codesandbox.io/s/material-ui-switch-ghost-issue-forked-fgqqx

Why might a react app perform poorly in the layout rendering stage?

In here we have boxes which can have children. If you click on a box, it will add a child for it.
https://codesandbox.io/s/suspicious-fire-e2jes
What I struggle to understand is why does it take increasingly long to create a child as the max depth increases?
According to performance audit, the problem is not in the data structure format where you define parent for a node, rather than defining all children for a node.
So the issue is layout. From what I could find, one anti-pattern is where you read-write-read-write styles over and over which forces browser to calculate layout before each read. I don't see anything of this sort in my code.
The issue is definitely layout. But not your layout.
I tried your code without loading your style.css file.
And it works as expected.
I created a similar use case without react. Pure css here with same dom structure:
const node = (id, children) => `
<div class='node'>${id}</div>
<div class='children'>
<div class='nodeAndChildren'>
${children}
</div>
</div>
`
document.getElementById("app").innerHTML = `
<div class='nodeAndChildren'>
${node(0, node(1, node(2, node(3, node(4, node(5, node(6, node(7, 'end'))))))))}
</div>
`;
.App {
font-family: sans-serif;
text-align: center;
}
.nodeAndChildren {
display: grid;
grid-auto-flow: column;
}
.node {
width: 100px;
border: 1px solid black;
}
.children {
display: grid;
}
<div id='app'/>
You can see how slow this is, even here.
This seems like a weird pattern matching issue for css or grid layout.
So, i tried it only with inline styling, was still slow
https://codesandbox.io/embed/displaygrid-bug-bcgsm
My suggestion is to use css columns instead of grid. Or just flex boxes with wrap.

Disable navigation item

I want to disable my navigation item using this code:
md-nav-bar
md-nav-item A
md-nav-item(ng-disabled=false) B
However, it doesn't work. Looking for the docs but couldn't find anything. How to disable navigation item in correct way?
I tried but couldn't find a proper way and didn't have to time to submit a pull/merge request (IF I could fix it that is). I did managed to get a work-around working, which basically intercepts and stops the click from propagating up to be handled by the mb-nav-item:
<md-nav-item md-nav-click="AppCtrl.gotoPage(1)" name="page-1">
<span ng-click="AppCtrl.gotoPageBlocker(1, $event)">Loan Details</span>
</md-nav-item>
Then in you controller/whatever, just define the gotoPageBlocker() like so:
function gotoPageBlocker(pageNo, theEvent) {
if (!userCanAccessPage(pageNo)) {
theEvent.preventDefault();
theEvent.stopPropagation();
}
}
This will intercept and either block or allow the click event to bubble up to the md-nav-item. Not the best way as using ng-disabled would be SOOO much easier + would have saved me a few hours mucking around, but it works for now. This will screw up the css a bit, so below is my "attempt" at making things look nice in css (well, scss):
.md-nav-item {
/**
* We need to move the margin and padding from the parent buttong to the child
* span. This is so the span intercepts the click from the whole area
*/
.md-button {
margin: 0px !important;
padding: 0px !important;
._md-nav-button-text span {
line-height: 24px;
margin: 0 4px;
padding: 12px 16px;
display: block;
outline: none;
}
}
/**
* This targets the unselected disabled button
*/
&[disabled] .md-button._md-nav-button.md-unselected {
color: #ff0000;
cursor: inherit;
}
}

Google maps markers labelClass animation not working properly

I am working on this project. I have an angular-google-map directive. I am overwritting defaults markers with labelClass.
CSS is working fine but does not hover.
.marker {
color: white;
border: 2px white solid;
border-radius: 50px;
text-align: center;
font-size: 20px;
letter-spacing: 0.1em;
font-weight: bold;
}
.marker:hover {
background-color: #C52183;
animation: pulse 1s;
}
/* ANIMATIONS */
#keyframes pulse {
50% {
transform: scale(4);
}
100% {
transform: scale(1);
}
}
If you check the example you can see an animation but not with real color. I sometimes get the real animation.
The full project is here.
pd: The problem can't be the animation, if i just try to change some css properties i dont get effect, so i think that the problem is with google maps and css.
I finally fixed this "bug".
The problem was on the MarkerWithLabel library but really isn't a bug, its just an impossibility (with this library). Checking the library we see :
// Set up the DIV for handling mouse events in the label. This DIV forms a transparent veil
// in the "overlayMouseTarget" pane, a veil that covers just the label. This is done so that
// events can be captured even if the label is in the shadow of a google.maps.InfoWindow.
// Code is included here to ensure the veil is always exactly the same size as the label.
this.eventDiv_ = document.createElement("div");
this.eventDiv_.style.cssText = this.labelDiv_.style.cssText;
I just modified the library so i don't create invisible div anymore and also i intercept events with real label. Now it's working under my requirements.
more info about the problem
demo working

Resources