Предотвращение повторного рендеринга компонентов с использованием ловушки useContext для установки глобального состояния проигрывателя - PullRequest
1 голос
/ 05 января 2020

Я пытаюсь реализовать 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

Я до сих пор не смог остановить эти повторные рендеры. Кто-нибудь может мне помочь с этим?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...