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);
@ekazakov
Copy link
Author

ekazakov commented Nov 11, 2021

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

Screen.Recording.2021-11-11.at.22.58.05.mov

@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