我正在尝试实现一个MiniGlobalPlayer,它在整个应用程序中都是全局的,并且某些组件可以设置MiniGlobalPlayer的状态。我认为React Context将是useContext和useReducer Hook的一个很好的解决方案。但是,重新渲染组件会使我的应用变慢( 延迟与使用useContext(setGlobalVideo )安装在DOM上的VideoItem组件的数量成比例。)
https://beebom.com/youtubes-new-ui-trick-is-a-mini-player-bar-for-collapsed-video-playback/(Youtube的MiniGlobalPlayer)
完整方案:
MiniGlobalPlayer的状态由VideoItem组件上的onPress()确定, 它使用useContext钩子从setGlobalVideo上下文中获取方法setVideo,该方法将设置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)
} )
我仍然无法阻止这些重新渲染。有人可以帮我吗?