-
-
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