How to memoize the component inside map loop in reactjs - reactjs

I'm creating a similar to netflix app. I have the below use case:
movies.map((movie) => (
<Grid item key={movie.imdbID}>
<Card movie={movie} onIconClick={onIconClick} />
</Grid>
))
I'm wrapping Card component under React.memo but even when one of the movie (watched flag inside it) changes, all the Cards get rendered.
Somehow Card is not getting memoized.
What I might be doing wrong?
How do I prevent re-rendering of all the movies when user has only clicked one card and only that card needs to be updated?

Related

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.

Updating react state in parent very slow

I'm developing a react application, and where I always thought the react state updates were really fast, I now found a problem.
I have a page view with a lot of elements on it, one of the elements is this one that gets loaded in the page:
<NotesCard notes={deal.notes} updateNotes={notes => {setDeal(prevState => ({...prevState, notes}))}} />
NotesCard is a child component that only renders a material-ui Card with another react component in:
export default function NotesCard(props) {
const {notes, updateNotes} = props;
return (
<Card className="Card">
<CardHeader
title="Notities"
/>
<CardContent>
<Notes notes={notes} onChange={updateNotes} />
</CardContent>
</Card>
);
}
Notes is the last component that renders a text field and just takes the props to the TextField:
function Notes(props) {
const {notes} = props;
function updateNotes(event) {
// props.deal.notes = event.target.value;
props.onChange(event.target.value);
}
return (
<div>
<FormGroup>
{notes !== null ?
<TextField
multiline
defaultValue={notes}
onChange={e => updateNotes(e)}
rows={3}
variant={'outlined'}
label={'Notities'}
/>
: 'Geen beschrijving...'}
</FormGroup>
</div>
);
}
Is there anything that I do wrong that creates a lot of lag? The page is a big page so it might have something to do with that, but I'd think that the updates performances would still be okay.
Your goal is to fix the slow render, only then you will want to take a look at number of rerenders if necessary.
Please install the react-dev-tools which contains an option to mark components when the are being rerendered. Alternativly you can also monitor the performance over a couple of seconds and investigate the rendering. This should help you understand what renders unnecessarily on your actions.
I see a potential problem with this one:
<Notes notes={notes} onChange={updateNotes} />
If you trigger onChange the parent state is mutated. This then causes ALL children to rerender. I would think that only the single will change and a change in this component wont effect other siblings. So try to move the state as close to where its used as possible. If you trigger onChange only the should be updated. This is something easy which can fix a ton of performance problems without using react features like Memo.

Avoid unneccessary re-rendering in React dynamic nested components

The state for my project includes a list of nested animals eg:
{"europe":{"air":{name:"warbler", points:0}}}
My components are generated based on this data, and at the lowest level (the animal itself), there is a button which is currently triggering a series of callbacks to the highest level, starting a dispatch to the reducer. Every time a button is clicked, all the components from every continent re-render.
Would it be better to implement useContext, even though every level of component requires some amount of data from the state object?
I tried to implement useCallback, to prevent re-rendering but I didn't know which callbacks were causing it. What would be the best way to optimize rendering a series of nested components (without redux)?
Inside App component
{Object.entries(state.animalData).map(([continent, areas]) => (
<Continent
key={continent}
areas={areas}
totals={state.totals.continents[continent]}
handleVote={(
num: number,
animal: string,
area: string
) => triggerChange(num, animal, area, continent)}
/>
))}
Inside Continent component
<Area
key={area}
area={area}
animals={animals}
onVote={(num: number, animal: string) =>
handleVote(num, animal, area)
}
/>
Inside Area component
{animals.map(animal => (
<Animal
key={animal.name}
animal={animal}
voted={(num: number) => onVote(num, animal.name)}
/>
))}
Inside Animal component
<div>
<h4>{animal.name}</h4>
<div>
<button onClick={voted(+1)}> Upvote </button>
<button onClick={voted(-1)}> Downvote </button>
</div>
<h4>{`${animal.points} Points`}</h4>
<hr />
</div>
Your components trigger a re render because you are using inline defined functions everywhere and those aren't referentially stable. You can use the useCallback (https://reactjs.org/docs/hooks-reference.html#usecallback) hook to prevent re renders.
But you could also use a context provider (as you suggested) which holds the voted callback. That way you don't have to use prop drilling to get the function to the component where it is needed.
The basic solution for this is explained in detail here: https://medium.com/#jeromefranco/how-to-avoid-prop-drilling-in-react-7e3a9f3c8674

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

What is causing my component to unmount?

I'm making an app where I have an Overlay component and a Map component rendering behind it just like this:
<div>
{showOverlay && <Overlay />}
<Map /> // this component should mount once
</div>
The overlay is shown to let the map load underneath but when I remove the Overlay (set showOverlay to false here) the Map component re-load.
At first I thought the Map component was just re-rendering, but after some digging I discovered that the component was actually re-mounting.
If I log in the componentWillMount, componentWillUnmount and render methods, the log appear in that order (which seems contradictory)
render
willUnmount
willMount
The parent does not re-mount, only the Map component do.
The Map component just render a div that reference a mapbox-gl-js map (like this https://gist.github.com/tristen/5c4b346ae38892f732504e6785d87057#file-map-js)
What can cause my component to re-mount itself like that ?
Thanks !

Resources