Last active
November 12, 2021 21:38
-
-
Save ekazakov/c0982f23f66d4b81415545644dc0f873 to your computer and use it in GitHub Desktop.
Оптимизация
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { AppRoute } from '../../const'; | |
import { Point } from '../../types/offers'; | |
import Header from '../header/header'; | |
import Map from '../map/map'; | |
import { useState } from 'react'; | |
import LocationsList from '../locations-list/locations-list'; | |
import { connect, ConnectedProps } from 'react-redux'; | |
import { bindActionCreators, Dispatch } from 'redux'; | |
import { changeCity } from '../../store/action'; | |
import { State } from '../../types/state'; | |
import NoPlaces from '../no-places/no-plases'; | |
import Places from '../places/places'; | |
import LoadingScreen from '../loading-screen/loading-screen'; | |
import { getCity } from '../../store/service-process/selectors'; | |
import { getIsDataLoaded, getOffers } from '../../store/service-data/selectors'; | |
const mapDispatchToProps = (dispatch: Dispatch) => bindActionCreators({ | |
onCityClick: changeCity, | |
}, dispatch); | |
const mapStateToProps = (state: State) => ({ | |
city: getCity(state), | |
offers: getOffers(state), | |
isDataLoaded: getIsDataLoaded(state), | |
}); | |
const connector = connect(mapStateToProps, mapDispatchToProps); | |
type PropsFromRedux = ConnectedProps<typeof connector>; | |
function MainScreen({ offers, city: cityName, isDataLoaded, onCityClick }: PropsFromRedux): JSX.Element { | |
// ---------> тут надо применить useMemo | |
const filteredOffers = offers.filter((offer) => offer.city.name === cityName); | |
const areFilteredOffers = Boolean(filteredOffers.length); | |
const city = areFilteredOffers ? filteredOffers[0].city : undefined; | |
// ---------> тут тоже useMemo | |
const points = areFilteredOffers ? filteredOffers.map(({ id, location }) => ({ id, location })) : []; | |
const [selectedPoint, setSelectedPoint] = useState<Point | undefined>(undefined); | |
// ---------> тут надо применить useCallback | |
const onListItemHover = (id: number) => { | |
const currentPoint = points.find((point) => point.id === id); | |
if (currentPoint) { | |
setSelectedPoint(currentPoint); | |
} | |
}; | |
if (!isDataLoaded) { | |
return <LoadingScreen />; | |
} | |
return ( | |
<div className="page page--gray page--main"> | |
<Header /> | |
<main className={`page__main page__main--index ${!areFilteredOffers ? 'page__main--index-empty' : ''}`}> | |
<h1 className="visually-hidden">Cities</h1> | |
<div className="tabs"> | |
<section className="locations container"> | |
{/* ---------> LocationsList перед экспортом нужно обернуть в memo */} | |
<LocationsList onCityClick={onCityClick} activeCity={cityName}></LocationsList> | |
</section> | |
</div> | |
<div className="cities"> | |
<div className={`cities__places-container container ${!areFilteredOffers ? 'cities__places-container--empty' : ''}`}> | |
{areFilteredOffers | |
? <Places points={points} cityName={cityName} filteredOffers={filteredOffers} onListItemHover={onListItemHover} /> | |
: <NoPlaces cityName={cityName} />} | |
<div className="cities__right-section"> | |
{city && | |
<Map city={city} points={points} selectedPoint={selectedPoint} screen={AppRoute.MAIN}></Map>} | |
</div> | |
</div> | |
</div> | |
</main> | |
</div> | |
); | |
} | |
export { MainScreen }; | |
export default connector(MainScreen); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Можно оптимизировать перерисовки по другому. Перерисовки по ховеру происходят из-за того, что setSelectedPoint вызывает перерисовку MainScreen и всех дочерних компонентов.
LocationsList — перерисовывается из-за того, что перерисовался родитель. Если обернуть его в memo, то он будет перерисовываться только при смене пропсов.
С Places чуть похитрее, он обернут в connect от редакса, а connect внутри сам использует memo. Но компонент все равно перерисовывается. Дело в том, что при перерисовке(т.е. вызове ф-и) MainScreen меняются пропсы для Places. Создается новый инстанс массива filteredOffers и на его основе points, а ф-я onListItemHover объявляется заново. Т.е. Places вызывается с новыми пропсами и мемоизация не помогает. Тут как раз нужны хуки useMemo и useCallback
Второй аргумент у useMemo и useCallback — это массив зависимостей, т.е. только при изменении этих переменных заново будут созданы инстансы filteredOffers, points и onListItemHover. Иначе будут использоваться уже мемоизированные инстансы