I am using react-animated-css, for simple animation in react. I have a list which I am rendering in a <ul> tag. I am adding animation to the first element of the list.
This list is getting updated every 3 seconds. And new element is added at the first position in the array.
The problem is, on load the animation is happening but not on update.
Here is full code : https://codesandbox.io/embed/ecstatic-cloud-qdpcj
I am not able to create a tag with 'react-animated-css' name as I am not eligible. It would be helpful if someone creates one for this.
You must define a key property for every element in a map. If you do not define one the array index is the default key. So the first element with key 0 will be reused after each render and only the last one will be added. If you define a key, the redrawing will based on the key value, and the first one will be added.
{items.map((d, i) => {
if (i === 0) {
return (
<Animated
key={d}
animationIn="bounce"
animationOut="flash"
animationInDuration={1000}
animationOutDuration={1000}
isVisible={true}
>
<li>{d}</li>
</Animated>
);
Here is the example: https://codesandbox.io/s/pedantic-darkness-vgp3f
From my point of view "react-animated-css" this library is not getting re-rendered once the state gets updated.
I have tried it with simple css animation property "#keyframe" and is working fine and getting re-rendered when state changes.
Code-sandbox link :
https://codesandbox.io/s/suspicious-dijkstra-h1q7u
Related
I have problem rendering page based on array of templates stored in redux. I'm unable to provide the actual code as it is too complex to reproduce in sandbox, but I will try with simplified structure.
I have pinterest-style grid based on flex-grow and js calculations for each item based on its dimensions so I need to have all of this inside one container.
I have 3 groups of items in same div:
blank_item + templates.map + tailItems.map
<div className="grid">
{shouldRenderBlankItem && <BlankItem />}
{templates.map((template) => (
<Template
key={template.uuid}
template={template}
/>
))}
{shouldRenderTail &&
tailItems.map(item, i) => (
<TailItem
key={i}
item={item}
/>
))}
</div>
The problem is - sometimes after render I have EXTRA children left from previous render and React puts them in front of other elements within div.grid so the result I have will look like:
3-4 of EXTRA <Template/> + blank_item + templates.map + tailItems.map
As a key for Template I use template.uuid that is coming from backend and I know they're unique and react also doesn't show any warnings for duplicated keys - so I know I'm not getting any duplicated items from API what I thought might be an issue.
Redux is fine - I see correct number of templates in it e.g. 50, grid and react dev tools shows same 50 templates coming as a prop when I inspect parent component, but the actual DOM has e.g. 53 elements in the same moment.
How can I debug issue like this? I suppose something is wrong during reconciliation but I don't know where exactly to start debugging this.
I have React/ReactDOM 16.13.1
Ok so in my case the problem was broken backend api which might return same template twice - so uuid's that I use for keys were unique for each template but they are not really unique in terms of the page and DOM elements.
I faced this only in production build that is why I didn't have duplicated key warning (on dev build I have another DB with much less templates so I was unable to reproduce this).
So anyone facing similar issue: go check your keys are really unique within page, because what happens after new templates load comes:
React wants to remove old item
It searches corresponding DOM element for each key and finds item with key="XXX" and removes it from DOM
Their duplicated items stays in DOM as corresponding key was processed and React doesn't care about this particular key anymore
We load new templates and they get appended to the parent container
Voila! We have old items before newly loaded
What I did here - my key now is:
templates.map((template, i) => <Template key={template.uuid + i} />
So in this case I am safe even if backend returns duplicated items.
Of course we will fix backend as well. Thanks for reading and helping!
Just quick example how to search for items with same id in data:
var duplicates = new Set();
data.map(t => t.id).forEach((id, i, arr) => {if(arr.filter(item => item == id).length > 1){
duplicates.add(id);
}});
console.log("DUPLICATED IDs:", duplicates)
Apologies for how this is worded as I don't know how to properly explain this problem.
I have the following piece of code. As it stands myArray has 10 elements in it.
if I have the <LeadingChild/> component above the .map, only the first of the 10 elements displays.
<Slider>
<LeadingChild/>
{myArray.map(x => (<Card cardTitle={x.title} shortDescription={x.shortDescription} Link= {x.link}></Card>))}
</Slider>
Removing the <LeadingChild/> component like below, allows the 10 elements to display just fine.
<Slider>
{myArray.map(x => (<Card cardTitle={x.title} shortDescription={x.shortDescription} Link= {x.link}></Card>))}
</Slider>
No errors are thrown.
I would like the end result to have both the leading child and all elements that exist within myArray to be displayed
Update:
I've created a new array of ReactNode's, that I've pre-populated with all the elements in myArray but with the <LeadingChild/> in the array as well.
This has resolved the problem but feels like a dirty solution.
I can't explain exactly why this happens, but I'm pretty sure it's because of the lack of key attribute in your mapped elements.
React uses key attributes for re-rendering comparisons and doesn't render elements with an existing key (elements meaning html lists' items). Some strange behaviour can occur when those list elements don't have a key attribute at all.
Try to add unique keys to your Card elements, like this:
{ myArray.map(elem => <Card key={elem.id} />) }
I am mapping of array of object which multiplies my component, its working properly but the problem is when I add another object to it somehow its hidden not sure why
{EditMultiObject.map((object, key) => (
<SubFormEditCom
key={key}
identifyNumber={key}
onEditBasicChange={onEditBasicChange}
onEditCalendarChange={onEditCalendarChange}
onEditChangeUrl={onEditChangeUrl}
lastObject={EditMultiObject.length === key + 1 ? true : false}
onEditAddNewObject={onEditAddNewObject}
/>
))}
You will noticed the first element is the initial object I set the second is the one I created when I click a add button. The style on the second div is missing(the newly added one) an inline style, note that I didnt even put an inline style in my code I assume it was added by react
I think I know now whats the solution for this I think its because of the third party library
I am trying to remove an item from an existing array using splice but is not working as expected. I even tried using filter instead of splice but got the same output. Can some one please take a look at the functionality here and help me figure out the issue.
Please try to add the available items here - https://08b11a0437.stackblitz.io/products then navigate to the cart page and try to remove each item. The items are not getting removed as expected.
Relevant Code is available in cartservice.ts, cartcomponent.ts(removeProductFromCart()) and cartcomponent.html - https://stackblitz.com/edit/08b11a0437?file=app%2Fcart%2Fcart.component.ts
The problem is you are removing items from an array in cartService, but for the UI you are using values from the products array.
A quick fix is to reload your products array. Just add the following code in your removeProductFromCart function in ShoppingCartComponent. Add it at the end of the function.
this._cartService.getAddedProducts().subscribe((data: IProduct[]) => {
this.products = data.map(item => item[0]);
});
Apart from that there is another issue. You are hiding items in ShoppingCartComponent html using *ngIf="removeItemId !== product.id". Consider you have three products (with id 1,2 and 3) in your cart, when the first products is removed, removeItemId will have 1, so the first product will be hidden. But when the user removes the second product, removeItemId will be set to 2, and now product one will be visible.
I have a number of Div's on a layout, each of them draggable, so there's the possibility of the user dragging them into positions where they will overlap.
I want to set the most recently made visible div to have a z-index value that's +1 from the last, and for this I'm using a $scope.nextIndex variable that's incremented each time one of the div's is made visible.
Each div has its own variable to track its own z-index value: $scope.one_zIndex, $scope.two_zIndex, $scope.three_zIndex, and each correctly gets assigned an incrementally larger value as each div is shown, hidden and shown again.
Each div has its own class: one_z, two_z, three_z
What I can't make work is assigning the variable to the z-index style on the divs themselves via the controller.
var myVar = document.getElementsByClassName("one_z");
If I log this to the console, I get what I expect - an array with one element [0]
[]
0 : div#one_z
length : 1
one_z : div#one_z
__proto__ : HTMLCollection
I would assume that I could set the z-index simply like this:
myVar[0].style.zIndex = $scope.one_zIndex;
However this throws an error:
Uncaught TypeError: Cannot set property 'zIndex' of undefined
What am I missing? Or is there a better way to accomplish this?
Here is a working plunker https://plnkr.co/edit/aaDipIWdqpajghc2F2Da?p=preview
You can set zindex via ng-style:
<div ng-style="{'z-index': $lastIndex;}">...</div>
This example completely useless in your case, but there is an example of ng-style usage. In your case I'd set index as property of you div element data source.
What wound up working was this:
First, I switched to IDs instead of classes, which likely didn't impact the solution but as each div was only ever going to exist once, IDs was the more correct way of identifying them.
Then, in the function that displays the div, I used this:
_.defer(function(){
jQuery('#one_z').css('z-index', $scope.one_zIndex);
});
There appeared to be an issue where I was showing the div and trying to set its z-index before the DOM had updated to include the div, so _.defer (I'm using Underscore.js) prevented the accessing of the z-index until everything had updated once, and it works.
As I mentioned in another comment - using ng-style wasn't working for me, and I had tried the _.defer with that approach as well initially without success.
For the record - I've used jQuery() instead of $() so it's clear in the code that this is a jQuery and not an Angular solution, and while I'd ideally have liked a purely Angular solution to this problem, this is clean enough for me, and doesn't do any DOM manipulation outside of Angular's purview that impacts the rest of the application in any way, it's purely display candy.