Я пытаюсь реализовать MiniGlobalPlayer, который был бы глобальным для всего приложения, и некоторые компоненты могут устанавливать состояние MiniGlobalPlayer. Я думал, что React Context будет хорошим решением с использованием useContext и useReducer Hook. Но повторная визуализация компонентов замедляет работу моего приложения ( Lag пропорционально количеству компонентов VideoItem, установленных на DOM с использованием useContext (setGlobalVideo )).
https://beebom.com/youtubes-new-ui-trick-is-a-mini-player-bar-for-collapsed-video-playback/ (MiniGlobalPlayer на Youtube)
Полный сценарий:
Состояние MiniGlobalPlayer определяется с помощью onPress () компонента VideoItem, который использует хук useContext для получения метод setVideo из контекста setGlobalVideo, который устанавливает состояние MiniGlobalPlayer
const areEqual = (prevProps, nextProps) => true
const VideoItem= React.memo((props)=> {
const context = useContext(setGlobalVideo)
return (
<View style={[
styles.flex, styles.column, styles.recommendation, styles.shadow,
{marginLeft: theme.sizes.margin },
]} key ={props.index}>
<View style={[styles.flex, styles.recommendationHeader]}>
<TouchableOpacity onPress={()=>{context.setGlobalVideo(props.video)}}>
<Image style={[styles.recommendationImage]} source={ {uri: props.video.Video_Pictures["0"]}} />
</TouchableOpacity>
<ExpensiveVideoRendering index={props.index} video={props.video}/>
</View>
</View>
);
}, areEqual);
export default VideoItem;
MiniGlobalPlayerProvider хранит глобальное состояние видео, и PlayerReducer может изменять его состояние в соответствии с отправленным действием. Действия отправляются компонентами VideoItem, которые присутствуют в различных частях приложения, для установки глобального состояния видео.
const MiniGlobalPlayerProvider=({children})=>{
const initialState ={
video: null,
isPlaying : false,
isBuffering: false
}
const [state, dispatch]= useReducer(PlayerReducer,initialState )
const {video} =state
function togglePodcast(podcast) {
animation = new Value(0);
timing(
animation,
{
toValue: video ? 1 : 0,
duration: 300,
easing: Easing.inOut(Easing.ease),
},
).start();
};
animation = new Value(0);
const setVideo=(video)=>
{
dispatch(
{
type: SET_GLOBAL_VIDEO,
payload: video
}
)
toggleVideo(video)
}
const translateY = animation.interpolate({
inputRange: [0, 1],
outputRange: [height, 0],
});
return (
<setGlobalVideo.Provider value={{setVideo}}>
<PlayerContext.Provider value={video}>
<StatusBar barStyle="dark-content" />
<View style={styles.container}>
<View style={StyleSheet.absoluteFill}>
{children}
</View>
{
!isOS && video && <VideoPlayer {...{video}} />
}
</View>
</PlayerContext.Provider>
</setGlobalVideo.Provider>
);
}
PlayerReducer. js
export default PlayerReducer=(state, action )=>
{
switch(action.type){
case SET_GLOBAL_VIDEO:
return {...state, video:action.payload}
default: {
throw new Error(`Unhandled action type: ${action.type}`)
}
}
}
Решения, которые я попытался уменьшить ненужные повторные рендеры: 1. Любой компонент, использующий useContext, повторно рендерится при изменении состояния провайдера. Итак, дорогие вычисления передаются внутреннему компоненту, который не перерисовывается с помощью React.memo () . 2. use useCallback ( Передать встроенный обратный вызов и массив зависимостей. UseCallback вернет запомненную версию обратного вызова, которая изменяется только в случае изменения одной из зависимостей. Это полезно при передаче обратных вызовов оптимизированному дочернему элементу Компоненты, которые полагаются на равенство ссылок для предотвращения ненужных визуализаций (например, shouldComponentUpdate).)
const setVideo=useCallback((video)=>
{
dispatch(
{
type: SET_GLOBAL_VIDEO,
payload: video
}
)
toggleVideo(video)
} )
Отдельные контексты (setGlobalVideo.Provider,
PlayerContext.Provider)
https://www.robinwieruch.de/react-prevent-rerender-component 5.
https://github.com/facebook/react/issues/15156 6.
https://github.com/facebook/react/issues/14110 Я до сих пор не смог остановить эти повторные рендеры. Кто-нибудь может мне помочь с этим?