Considering code below (test example) I was thinking what is the best way to manage update of objects in React/JS. Idea is that there is a list of Origin Airports that have airplanes in them. When origin airport is toggled list of destination airports are rendered. When destination airport is selected in that list, airplanes from that airport go to favorite list.
Airplanes in origin or destination airport can be selected all at once (toggle) or one by one (not implemented yet). So what is the best option to manage selection knowing that airport list styling depends on how many airplanes are in airport. Also if there is favorite airplanes, they should be gone in case of origin airport is unselected (removing airplanes from destination airports and favorite list).
type Airplane = {
name: string;
currentLocation: { airport: string; latitude: number; longitude: number };
destination: { airport: string; latitude: number; longitude: number };
};
type Airport = {
name: string;
latitude: number;
longitude: number;
airplanes: Airplane[];
};
const AirplanesManager = () => {
const [airplanes, setAirplanes] = useState<Airplane[]>([]);
const [selectedAirplanes, setSelectedAirplanes] = useState<Airplane[]>([]);
const [favoritePlanes, setFavoritePlanes] = useState<Airplane[]>([]);
const uniqueOriginAirports = useMemo(
() =>
airplanes.reduce((airports, airplane) => {
let airport = airports.get(airplane.currentLocation.airport);
if (!airport) {
airport = {
name: airplane.currentLocation.airport,
latitude: airplane.currentLocation.latitude,
longitude: airplane.currentLocation.longitude,
airplanes: [],
};
airports.set(airplane.currentLocation.airport, airport);
}
airport.airplanes.push(airplane);
return airports;
}, new Map<string, Airport>()),
[airplanes],
);
const uniqueDestinationAirports = useMemo(
() =>
selectedAirplanes.reduce((airports, airplane) => {
let airport = airports.get(airplane.destination.airport);
if (!airport) {
airport = {
name: airplane.destination.airport,
latitude: airplane.destination.latitude,
longitude: airplane.destination.longitude,
airplanes: [],
};
airports.set(airplane.destination.airport, airport);
}
airport.airplanes.push(airplane);
return airports;
}, new Map<string, Airport>()),
[selectedAirplanes],
);
const toggleAirplanes = (airplanes: Airplane[]) => {
setSelectedAirplanes(current =>
airplanes.reduce(
(updated, airplane) => {
const index = updated.indexOf(airplane);
if (index >= 0) {
updated.splice(index, 1);
} else {
current.push(airplane);
}
return updated;
},
[...current],
),
);
};
const toggleFavorites = (airplanes: Airplane[]) => {
setFavoritePlanes(current =>
airplanes.reduce(
(updated, airplane) => {
const index = updated.indexOf(airplane);
if (index >= 0) {
updated.splice(index, 1);
} else {
current.push(airplane);
}
return updated;
},
[...current],
),
);
};
const removeFromFav = (airplaneToRemove: Airplane) => {
setFavoritePlanes(current => current.filter(airplane => airplane !== airplaneToRemove));
};
const isOriginSelected = (airportName: string) => {
return uniqueOriginAirports.get(airportName)!.airplanes.some(airplane => selectedAirplanes.includes(airplane));
};
return (
<div className="material-background page-wrapper">
<div>
{Array.from(uniqueOriginAirports.entries()).map(([airportName, airport]) => (
<div
key={airportName}
style={{ backgroundColor: isOriginSelected(airportName) ? 'green' : 'white' }}
onClick={() => toggleAirplanes(airport.airplanes)}
>
{'...'}
</div>
))}
</div>
<div>
{Array.from(uniqueDestinationAirports.entries()).map(([airportName, airport]) => (
<div key={airportName} onClick={() => toggleFavorites(airport.airplanes)}>
{'...'}
</div>
))}
</div>
<div>
{favoritePlanes.map((airplane, i) => (
<div key={i} onClick={() => removeFromFav(airplane)}>
{airplane.name}
</div>
))}
</div>
</div>
);
};
export default AirplanesManager;
Option 1
Keep three arrays and reduce entries in those array to unique airports and then do some checks during the render (check if there is any selected airplanes in airport to apply different styling). This option is quite easy to manage as you need to update one list only (for example selectedAirplanes) and other lists will be updated due to their dependencies.
Option 2
Keep all information in Airplane object and have only one array of airplanes, rendering lists and unique airports with airplanes in them according to properties of airplane itself.
type Airplane = {
name: string;
currentLocation: { airport: string; latitude: number; longitude: number };
destination: { airport: string; latitude: number; longitude: number };
isSelected: boolean;
isFavorite: boolean;
};
Option 3
Keep option 2 model, but update airports manually (adding, removing airplanes from airport airplanes array).
So is there any best practices in Javascript/React how to manage these kind of updates? I noticed that keeping separate array is easier for readability and update than accessing object properties (as you need keep in mind that other object are dependent on properties changes), but it is easier to render components as you always have one array of objects that keep state of their selection or belonging to favorite list as their properties?