Прежде чем вы погрузитесь в чтение моего решения, я хочу пригласить вас в свой Telegram-канал. Там можно найти хорошие предложения удаленной работы. Этот канал создан только для разработчиков.
Начинаем
Работая с React Native, иногда нам нужно найти способ прокрутки до определенного элемента. Это просто, когда scrollview и все необходимые элементы лежат в одном дереве DOM.
Представьте себе случай, у вас в приложении есть Drawer Navigation, по нажатию на определенную кнопку из бокового меню, приложение должно закрыть sidebar, перейти на нужный экран и перейти к определенному элементу. Мы понимаем, что кнопка из панели навигации, scrollview и необходимый элемент основаны на разных деревьях DOM.
Моё решение
class Refs {
private _elements = new Map();
public setRef = (key: string) => (ref: any) => {
this._elements.set(key, ref);
};
public getRef(key: string) {
return this._elements.get(key);
}
public remove(key: string) {
this._elements.delete(key);
}
public clear() {
this._elements.clear();
}
}
export default new Refs();
Теперь у нас есть класс, в котором мы можем хранить ссылки на необходимые элементы и манипулировать ими. Если у вас есть вопросы по поводу моих решений, задавайте их в Telegram Channel!
Создадим Drawer Navigation и воспользуемся методом setRef
import React, {FC} from 'react';
import {View, SafeAreaView} from 'react-native';
import Refs from '../../../helpers/refs';
import {
DrawerContentScrollView,
DrawerContentComponentProps,
} from '@react-navigation/drawer';
import DrawerHeader from './drawerHeader';
import DrawerMenu from './drawerMenu';
import DrawerButtons from './drawerButtons';
import styles from './styles';
const DrawerContent: FC<DrawerContentComponentProps> = ({navigation}) => {
Refs.setRef('navigation')(navigation);
// Запишем ссылку на навигацию в обьект нашего класса
return (
<View style={styles.menu}>
<DrawerContentScrollView>
<DrawerHeader />
<DrawerMenu />
</DrawerContentScrollView>
<SafeAreaView>
<DrawerButtons />
</SafeAreaView>
</View>
);
};
export default DrawerContent;
Создадим ScrollView и добавим ссылку на его Ref с помощью setRef
import React, {FC, useEffect} from 'react';
import {View, SafeAreaView, ScrollView} from 'react-native';
import {DrawerContentComponentProps} from '@react-navigation/drawer';
import Refs from '../../helpers/refs';
import Statistics from '../../components/statistics';
import Header from '../../components/header';
import Goals from '../../components/goals';
import Offers from '../../components/offers';
import styles from './styles';
const Main: FC<DrawerContentComponentProps> = ({navigation}) => {
useEffect(() => {
return () => Refs.remove('scroll'); // remove scroll on unmount
}, []);
return (
<SafeAreaView style={styles.body}>
<ScrollView ref={Refs.setRef('scroll')}> // add scroll to Refs
<View style={styles.statisticsBar} />
<Statistics />
<Header {...navigation} />
<Goals />
<Offers />
</ScrollView>
</SafeAreaView>
);
};
export default Main;
Создадим компонент Offers и добавим ссылку на его Ref с помощью setRef
import React, {useEffect, useMemo, useContext} from 'react';
import {View} from 'react-native';
import Refs from '../../helpers/refs';
import {TabsContext} from '../../providers/tabs';
import Tabs from '../tabs';
import OffersList from './list';
import styles from './styles';
const OffersView = () => {
const [tab] = useContext(TabsContext);
useEffect(() => {
return () => Refs.remove('offers'); // remove reference link
}, []);
return (
<>
<Tabs />
<View style={styles.offers} ref={Refs.setRef('offers')}> // add offers view element to Refs
<OffersList />
</View>
</>
);
};
export default OffersView;
Пишем метод scrollToOffers
import Refs from '../../../helpers/refs';
const scrollToOffers = () => {
const scroll = Refs.getRef('scroll');
const offers = Refs.getRef('offers');
const navigation = Refs.getRef('navigation');
offers?.measure((x: number, y: number) => {
navigation?.jumpTo('Main');
scroll?.scrollTo({x, y, animated: true});
});
};
Обязательно почистим за собой Refs class
import React, {useEffect} from 'react';
import Refs from './helpers/refs';
import DrawerNavigation from './stack/drawer';
function App() {
useEffect(() => {
return () => Refs.clear(); // clear whole map from Refs class
}, []);
return (
<DrawerNavigation />
);
}
export default App;
Подведем итоги
Почему я не использовал HOC или хуки, чтобы закрыть эту задачу?
Потому что я не хочу повторно рендерить какие-либо компоненты. Основная цель состояла в том, чтобы найти способ прокрутки в условии, когда элементы находятся на разных уровнях. В этом случае мне не нужны обновления DOM, это означает, что мы можем использовать ссылки для существующих элементов и манипулировать ими.
Подпишитесь на мой Telegram канал, здесь вы найдете предложения удаленной работы с высокими рейтами.