Skip to content

Instantly share code, notes, and snippets.

@ekazakov
Last active November 12, 2021 21:38
Show Gist options
  • Save ekazakov/c0982f23f66d4b81415545644dc0f873 to your computer and use it in GitHub Desktop.
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);
@Va-leri
Copy link

Va-leri commented Nov 12, 2021

Когда наводишь курсор на карточку, то перерисовываются все компоненты на странице(см синие рамки вокруг компонентов). Можно избежать перерисовки всех компонентов кроме карты
Screen.Recording.2021-11-11.at.22.58.05.mov

Я сейчас список офферов и карту вынесла в отдельный компонент. Таким образом список городов сверху у меня уже не перерисовывается при наведении на оффер. Чтобы избежать перерисовки и списка самих офферов, я так понимаю, нужно выносить selectedPoint в стор. А мы вроде обсуждали, что почему-то требуется его хранить в самом компоненте

@ekazakov
Copy link
Author

Чтобы избежать перерисовки и списка самих офферов, я так понимаю, нужно выносить 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. Иначе будут использоваться уже мемоизированные инстансы

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment