-
-
Save ekazakov/c0982f23f66d4b81415545644dc0f873 to your computer and use it in GitHub Desktop.
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); |
Когда наводишь курсор на карточку, то перерисовываются все компоненты на странице(см синие рамки вокруг компонентов). Можно избежать перерисовки всех компонентов кроме карты
Screen.Recording.2021-11-11.at.22.58.05.mov
Я сейчас список офферов и карту вынесла в отдельный компонент. Таким образом список городов сверху у меня уже не перерисовывается при наведении на оффер. Чтобы избежать перерисовки и списка самих офферов, я так понимаю, нужно выносить selectedPoint в стор. А мы вроде обсуждали, что почему-то требуется его хранить в самом компоненте
Чтобы избежать перерисовки и списка самих офферов, я так понимаю, нужно выносить selectedPoint в стор.
Это вариант, но не самый лучший. В стор стараются не выносить лишние данные, т.к. он разростается и им становится сложней управлять.
Можно оптимизировать перерисовки по другому. Перерисовки по ховеру происходят из-за того, что setSelectedPoint вызывает перерисовку MainScreen и всех дочерних компонентов.
LocationsList — перерисовывается из-за того, что перерисовался родитель. Если обернуть его в memo, то он будет перерисовываться только при смене пропсов.
С Places чуть похитрее, он обернут в connect от редакса, а connect внутри сам использует memo. Но компонент все равно перерисовывается. Дело в том, что при перерисовке(т.е. вызове ф-и) MainScreen меняются пропсы для Places. Создается новый инстанс массива filteredOffers и на его основе points, а ф-я onListItemHover объявляется заново. Т.е. Places вызывается с новыми пропсами и мемоизация не помогает. Тут как раз нужны хуки useMemo и useCallback
const filteredOffers = useMemo(() => offers.filter((offer) => offer.city.name === cityName), [offers, cityName]);
const points = useMemo(() => areFilteredOffers ? filteredOffers.map(({ id, location }) => ({ id, location })) : [], [filteredOffers]);
const onListItemHover = useCallback((id: number) => {
const currentPoint = points.find((point) => point.id === id);
if (currentPoint) {
setSelectedPoint(currentPoint);
}
}, [points]);
Второй аргумент у useMemo и useCallback — это массив зависимостей, т.е. только при изменении этих переменных заново будут созданы инстансы filteredOffers, points и onListItemHover. Иначе будут использоваться уже мемоизированные инстансы
Когда наводишь курсор на карточку, то перерисовываются все компоненты на странице(см синие рамки вокруг компонентов). Можно избежать перерисовки всех компонентов кроме карты
Screen.Recording.2021-11-11.at.22.58.05.mov