Loading dynamic links based on state - reactjs

I'm having trouble rendering a dynamic ReactRouter menu.
I have a component, <MyApp />. In the componentDidMount() method of <MyApp />, i'm calling an API. the API returns an array of articles. I then set the state of <MyApp /> with these new articles.
.....
this.setState({
isLoaded: true,
articles: result
});
Now, I load a new component, <ArticlePage articles={this.state.articles} />
Slight tangent - This new <ArticlePage /> is made with a function component, not a class component. The reason being, is I had trouble using ReactRouter inside Class components.
function ArticlePage(props){}... now exists. Props holds my array of articles.
Inside ArticlePage(), i want to create a reactRouter link for each of the articles in the array that i'm passing in as props. This is where i'm having trouble.
In my head, I need to loop over each of the props.articles array elements, and create a reactRouter link element..
<ul>
{props.articles.map(item =>
(
<li>
<Link to={`${url}/${item.article_id}`}>{item.title}</Link>
</li>
))}
</ul>
The problem is, my links are not being displayed on the page. Debugging suggests that when the page renders and the .map loop above runs, the array is empty. Using Chrome developer tools, I can see that the props.article for that component does in deed contain all the articles. However, i am of course looking at this object in slower time than when the page renders, so it has had time to complete.
What is it, that I am doing wrong? Perhaps I should be loading and passing the articles state in a different way?

Related

Why infinite scroll pagination uses useState not using DOM append instead?

I have seen many tutorials and articles which use useState to store all the records on a state instead of using ReactDOM or something to add HTML elements.
For example, they keep previous data and merge with new data and after that set in a useState hook
return [...new Set([...prevBooks, ...res.data.docs.map(b => b.title)])]
I have 2 questions
Isn't it a performance problem to have all the records in the state and to print it in JSX by map? There may be millions of records.
If I want to add a React component to the end of a div#ID, what should we do in the pagination?
For example, I have this block code:
<article className="col-sm">
<div className="row client-home-header-post-article-row" id="BlogsPosts">
{posts.entries.map((item) => (
<BlogItem post={item} key={item.id} size={4} />
))}
</div>
</article>
And with an action function like const showMore, add another <BlogItem post={item} key={item.id} size={4} /> to the bottom of BlogsPosts ID (like e.append)
I saw this post Append component in React, he suggested without recommending to use ReactDOM.createPortal, but I tested it like this, and it does not work
ReactDOM.createPortal(
<BlogItem post={item} key={item.id} size={4} />,
document.querySelector("#BlogsPosts")!
)
The posts I saw:
Append component in React
Reactjs append an element instead of replacing
How do you append a React Component to an html element
How to append React components to HTML element using .append()
How can I append a React component to an html element i?
Using document.querySelector in React? Should I use refs instead? How?
Append Element to an Existing Element React
Thank you in advance
Isn't it a performance problem to have all the records in the state
and to print it in JSX by map? There may be millions of records.
It depends. A performant server should try to keep the metadata it returns to the browser for each book small. That is, each book object should try to to contain minimal information like a title, author, price, and thumbnail URL, not all the data that the database knows about the book. Something like this:
{
"title":"MyBook",
"author":"Jeff Bezos",
"price":19.99,
"price_unit":"USD",
"thumbnail":"https://websitename.s4-bucket.region.UUID-1234"
}
Reducing the amount of data will boost performance by making searching through this data faster on the browser. Each object of this size is also less than 500 bytes, so if you are obtaining 50 items at a time you're only retrieving 25KB data per request, which with modern speeds only takes a few milliseconds.
If I want to add a React component to the end of a div#ID, what should
we do in the pagination?
You can use a useMemo hook which will update the BlogItem components you have rendered when the posts change. Something like this should work:
const blogItems = useMemo(()=>posts.entries.map((item) => (
<BlogItem post={item} key={item.id} size={4} />
)), [posts.length])
Then in your JSX just do this:
<article className="col-sm">
<div className="row client-home-header-post-article-row" id="BlogsPosts">
{blogItems}
</div>
</article>

How to force one react component to not re-render?

I have a main react component (The entire page) that contains several other UI items in it. One of those UI items is an OpenLayers map. Another component is a text field that contains the latitude and longitude of the current mouse pointer position.
I want the text field to update whenever the mouse pointer moves on the map. In my code's current state, this works.
The problem is every time the mouse moves and I update the react state value for the latitude/longitude (which gets rendered in the text field as expected) the map re-renders. This looks terrible because the map flashes every time as it begins to load the map tiles.
So I have the mouse latitude/longitude position, but I can't get the map to not flash/stutter/strobe.
Is there a way to get a mouse event from the map component, and send it to another component on the same screen without causing the render method to redraw the map every time? Or is React the wrong tool for this project?
As a note, I'm using React Redux to handle some state values, such as the map object. All of my child components are React functional components, but the main component for the page is a React class component.
If it helps, I set the mouse listener on the map like this:
this.props.map.on('pointermove', event => {
this.setState({
mouseCoordinates: (event.coordinate),
});
});
This is my render method:
render() {
return (
<div className="main">
<p className={noMargPad}>
{this.state.mouseCoordinates}
</p>
<Map
center={fromLonLat(this.state.center)}
zoom={this.state.zoom}
>
<Layers>
<TileLayer source={osm()} zIndex={0} />
{this.state.showLayer1 && (
<VectorLayer
source={vector({
features: new GeoJSON().readFeatures(geojsonObject, {
featureProjection: get("EPSG:3857"),
}),
})}
style={FeatureStyles.MultiPolygon}
/>
)}
{this.state.showMarker && (
<VectorLayer source={vector({
features: this.state.features
})} />
)}
</Layers>
<Controls>
<FullScreenControl />
</Controls>
</Map>
</div>
);
}
One of the main advantages of using a tool like React is it will only refresh the components in the render method that have had an update to their dependent data. See this article for more information.
The fact that your map is rendering again indicates that the map, or one of its children is being passed a piece of data that keeps changing.
Based on your code, one possibility is the call to OSM(), since that's a constructor from the OpenLayers API. A new object/class instance in JavaScript could cause data re-render.

ReactDOM.render does not re-render the 2nd element on the list

im having problem with ReactJS, i
on index.js i have:
ReactDOM.render([<App />, <Footer />], document.getElementById('root'));
the problem is that Footer needs to add an 80px padding from the bottom only on certain pages
so on the render method i do
<div>
{isMobile && window.location.pathname.startsWith('/summary') &&
<div style={{height: 80}}></div> }
</div>
but it doesn't re-render when window.location.pathname changes,
i move around the web app and it doesn't change only when i hit F5 it renders correctly on that page.
i tried using events on window but they're aren't invoked as well...
window.addEventListener('locationchange', function(){
console.log('xxxxxxx location changed!');
})
window.addEventListener('hashchange', function(e){console.log('xxxxxx hash changed')});
window.addEventListener('popstate', function(e){console.log('xxxxxxx url changed')});
how i can make it re-render? or make Footer work as React component that can render ?
This is a common problem!
Out of the box, React is configured to operate on a single page (see Single Page Applications), which basically means it deletes what's shown on screen and renders new information when an update is required.
Naturally, simulating the routing behavior that's observable for non Single Page Apps is a little more difficult - check out React Router, which is a library created to tackle this exact issue.

Can one React component render by 2 classes?

Can one React component render by 2 classes? Just like I did in the picture.
I tried the above. It gives me another error Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of "Groups".
The button component Im using in Groups method(Groups.jsx) like this way.
const Groups = (props) => (
<div className = 'panel'>
<h2>Groups</h2>
<button >Get Groups</button>
<div className = 'group-list'>
{props.groups.map((group) =>
<GroupsEntry name = {group.name}
members = {group.members}/>
)}
</div>
</div>
);
Do you guys have any idea about this? Thank you
I will try to clarify a little.
You can render a component from whatever parent component you want.
By in the case of your picture, what is telling you that the first component in tree, was App.js, and then App.js rendered Groups.js component, and Groups.js rendered your actual component.
In the same page, the warning you are seeing about using "key" is because you need to set a unique key value for each element that you are rendered as a list, a repeated item. This is because the internal react work to compare if it has to rerender again your component needs it. You will have performance problems (not in an easy example...) if you dont add it. Normally you use the id of the object you are rendering.
I hope i clarified a little.
Yes, a component can be rendered as many times as you would like. The issue is that you are mapping over an array and returning an element. React requires that you put a unique key prop on these elements that ideally are consistent between renders.
You can try to update your code to be something like the following:
const Groups = props => (
<div className="panel">
<h2>Groups</h2>
<button>Get Groups</button>
<div className="group-list">
{props.groups.map(group => (
<GroupsEntry key={group.name} name={group.name} members={group.members} />
))}
</div>
</div>
);
This is assuming group.name is unique. If you have a unique identifier (eg: group.id) that would be ideal.
For more examples and why this is necessary you can checkout the official docs: https://reactjs.org/docs/lists-and-keys.html

Twinkling images in a component

I have component with navigation, on click item to child component passed in props some params, one of params - object 'itemImage' with className and url, like this:
{
url: '/static/image.svg',
className: 'absolute hidden md:block min-w-53 lg:min-w-68 mt-30 lg:mt-19 -ml-28 lg:-ml-75',
}
In child component ItemComponent:
{
itemImage &&
<img className={itemImage.className} src={itemImage.url} alt='' />
}
ItemComponent is selected from an array according to the order of the element in navigation (it is responsible for the object passed to the child component), since the list of navigation elements and elements of the array of pictures are not related and of different lengths. The sample is implemented on the basis of an index in map for objects and an index in an array with pictures, to be more precise.
Problem:
the pictures flicker as the state of the parent and the child is updated, is it possible to somehow avoid this and make the change of the picture clear without the flickering of the previous card.
You can use the below-mentioned code to render the Array of Images.
<>
{this.props.Images.map(item=>{
return (item)
})}
<p>
{JSON.stringify(this.state)}
</p>
<p>
{JSON.stringify(this.props.changed)}
</p>
<button onClick={this.props.onChange}>Change</button>
<button onClick={this.onCurrentChange}>Current State Change</button>
</>
Please check the demo here Demo
You can make somethings to try to prevent that.
1- Add a key prop to the elements. It help react understand that it is the same data from before and not re-render that piece.
2- Use react PureComponent on the flickering element https://reactjs.org/docs/react-api.html#reactpurecomponent to prevent the re-render
3 - Instead of purecomponent implement shouldComponentUpdate

Resources