React keeps children from previous render - reactjs

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)

Related

Return to previous page on exact point in React

Imagine I have page A with very long list of rows. I scroll down to e.g. row number 500. The row is also link to page B, that shows row details. After examinig the details on page B, I want to go back to page A, but I want to return to exactly same point, from where I went to page B, that means on the row 500. How can I achieve this behavior in ReactJS app?
This can be achieved in two ways.
One simple use of html id attributes.
With the help of react-router-dom and props passing.
I have listed the method 1 here which is beginner friendly
This can be achieved with the help of id's
I will list a sample html code which works similar at jsx
<div>
<p id="1">Row 1 </p>
<p id="2">Row 2 </p>
...
...
...
</div>
Let's suppose the use of the page is www.samplepage.com
Instead of this link if I made it specific i.e., www.samplepage.com#1
it will take me to the Row 1 it is like anchor tag in same page
you can store the tags in state and use it.
You can also see similar implementation at github
First pass your current path with query parameter
<Link to={{pathname: `/apps/calendar`, query: `/apps/personnel/view/${selectedPersonnel.id}`}} >
Second use this parameter in redirected component with following code
const CalendarComponent = props => {
const backUrl = props.location['query']
}

Adding a child element before myArray.map(x => ) causes only one child to be rendered from the array

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} />) }

react-animated-css not fired when list is updated

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

How to automatically add an unique data attribute to all elements in React?

I'm testing my React application using Selenium IDE and want to add an unique ID ("data-testid" for example) to all elements so I'd be able to select them easily.
So far I encountered libraries which accomplish that by adding some code (react-html-id for example), but I'm looking for a solution which will add these data attribute automatically on build time.
For Example:
Given a component
<SomeComponent /> // returns <span>Hello World<span>
When building the React application, it should be resulted with:
<span data-testid="123">Hello World</span>

React : best way to inject Component in dynamically loaded HTML?

I'm new on React (I more at ease w/ jQuery or AngularJS). I have a special case and I don't find a good way to resolve it...
My app contains an area which is like a "document viewer". It loads an HTML content from the backend (via API, using Fetch) and inject it in the "viewer" component. The HTML content loaded looks like an "university report" (it's just a formatted text, only <span> and <p> with class="..." attributes, nothing more).
Ex : <p>Lorem ispum <span>some text</span> loreb bis <span>ipsum</span></p> ...
I load the content, and inject it this way in the render() of my component <Viewer> :
<div dangerouslySetInnerHTML={ getFreshlyLoadedHTML() } />
Easy, it works just fine !
But... Now, I want to inject some "interactive" components in the loaded HTML. For example, some button to give a feedback etc. The API must decide where to place the component between the words/nodes of the formatted text (HTML).
Ex :
<p> Lorem ispum <span>some text</span>
loreb bis <span>ipsum</span>
<MyFeedbackButton paragraph="1.3"/>
</p><p>Other Lorem Ipsum<p><span>...</span>
There, I'm stucked because I cannot use dangerouslySetInnerHTML if there are components inside the loaded HTML...
First attempt : I've tried modifying the API, and instead of sending the HTML in a string to the app, I send a custom JSON structure that represents almost the final JSX structure that I want. Then, in my react page, the render function only have to parse the JSON and build the JSX (here, a JsFiddle example if it's not clear : https://jsfiddle.net/damienfa/69z2wepo/34536/ )
It works, but I can't believe it's the good way...
I see a major problem : all the HTML node (span, p...) that I build from the render function are referenced by reactJs, is it really necessary ? Mostly, there are "dead" nodes (I mean, dom node that won't never changed, this is static formatted text).
Just take a look a all those "data-reactid" on nodes that never will be interactive...
What would be your advice on that case ?
What about my attempt with a JSON-structure sent by the API ?
Is there a way to say to react "do not reference that element" ?
Do you clearly see a better solution to my problem ?
Your current workflow is not very secure and subject to many potential errors and open doors, especially concerning code injection ...
The overload due to react tracking the nodes is not an issue, React could track 10 000 nodes and not have a problem (well actually on many of my apps React has more than 100 000 nodes to care about and it still rurns perfectly).
I see different solutions here:
If there are only 3 or 4 possibilities of dynamic components and order, you might have components like "templates" to which you would simple send text arguments. This is the safest and easiest option.
If it doesn't suit your use-case but the JSON file can contain only a limited set of components, the components should be located in your main app, and then rendered with custom props from the JSON. Actually given the structure of data you could consider using xml instead of json and build a xml tree that you would parse and render. Only components from your white list would be rendered and it would limit drastically the potentials security issues. If needs quite some work on the XML parser though.
If the JSON file can contain many many different and unpredictable components or if the behaviour of those components is largely dynamic and independant of your app, you might as well consider using an iframe, with its own JS and HTML, so that this part of the code is isolated from the rest.
Try using an inline anonymous function within the inner content from within React using JSX. It works! Just be careful about how you wire up the data so there isn't a route where a user can inject HTML from an input or text field.
<div className="html-navigation-button">{(() =>
{
const CreateMarkup = ( sNavItemName :string ) => {
return {__html: sNavItemName };
}
var sTextToAddHtmlTo = props.nextNavItem.name.toString();
sTextToAddHtmlTo = sTextToAddHtmlTo.replace( "/", "/<wbr>" );
return (
<div dangerouslySetInnerHTML={CreateMarkup( sTextToAddHtmlTo )} >
</div>
);
})()}
</div>
I didn't override the React internals of 'render()', but only used a React Component with props wiring to pass down data to it for rendering.
I added the hook for 'dangerouslySetInnerHTML' deep within the return content of the React Component so there would be no easy way to intercept and manipulate it.
As such, there is no 100% guarantee on safety, but that's where adding good security to web services, databases, and use of CORS and CORB would be helpful to lock down security risks.

Resources