React native 如果通过子组件调用,则Redux属性增量不起作用

React native 如果通过子组件调用,则Redux属性增量不起作用,react-native,react-redux,react-native-android,react-native-flatlist,react-thunk,React Native,React Redux,React Native Android,React Native Flatlist,React Thunk,我有一个平面列表,上面有图像、视频。对于图像,我定义了显示的持续时间,然后移动到平面列表中的下一项,但对于视频,一旦视频结束,就移动到下一项 我正在对currentlyPlayingIndex和flatlist数据源使用Redux。 若我在flatlist中只有图像,那个么它可以正常工作,但若我有一个视频,在视频端,我需要将事件从子事件传递给其父事件。父级调用与图像持续时间结束相同的方法移动到下一个索引,但在视频结束的情况下,currentlyPlayingIndex不会增加1。 父组件代码或f

我有一个平面列表,上面有图像、视频。对于图像,我定义了显示的持续时间,然后移动到平面列表中的下一项,但对于视频,一旦视频结束,就移动到下一项

我正在对currentlyPlayingIndex和flatlist数据源使用Redux。 若我在flatlist中只有图像,那个么它可以正常工作,但若我有一个视频,在视频端,我需要将事件从子事件传递给其父事件。父级调用与图像持续时间结束相同的方法移动到下一个索引,但在视频结束的情况下,currentlyPlayingIndex不会增加1。 父组件代码或flatlist处理程序

 import React, { Component } from 'react'
import { TouchableWithoutFeedback, View,StatusBar,Dimensions,FlatList } from 'react-native'
import {FileType,getFileType,getFileExtension} from '../services/FileManagerService'
import VideoPlayer from '../components/PlayerTypes/VideoPlayer'
import ImagePlayer from '../components/PlayerTypes/ImagePlayer'
import PdfPlayer from '../components/PlayerTypes/PdfPlayer'
import { ScaledSheet } from 'react-native-size-matters';

import NothingTpPlay from '../components/PlayerTypes/NothingTpPlay'
//redux
import {bindActionCreators} from 'redux';
import { connect } from 'react-redux';
import * as Actions from '../database/actions/ScheduleActions.js'; //Import your actions
import * as Animatable from 'react-native-animatable';
import constants from '../config/constants'
import KeepAwake from 'react-native-keep-awake';
import AudioPlayer from '../components/PlayerTypes/AudioPlayer'
import { showToastMessage } from '../utils/ToastMessage'
import I18n from "../locales/i18n-js";
import WebsitePlayer from '../components/PlayerTypes/WebsitePlayer'
import FullScreen from "../NativeBridgingHeader/FullScreen";

let deviceWidth = Dimensions.get('window').width
let deviceHeight = Dimensions.get('window').height


 class PlaylistPlayerScreen extends Component {
    constructor() {
        super();
        this.state = {
          currentVisibleIndex:-1
        }
      this.playNextFile = this.playNextFile.bind(this)
      this.videoEnded = this.videoEnded.bind(this)
        this.schedulePlayDurationTimer = this.schedulePlayDurationTimer.bind(this)
        this.viewabilityConfig = {
          waitForInteraction: false,
          itemVisiblePercentThreshold: 99,
        }
      }

    static navigationOptions = {
        header: null,
    };

    onViewableItemsChanged = ({ viewableItems }) => {
      // viewableItems will show you what items are in view
      // console.log("onViewableItemsChanged called" + JSON.stringify(viewableItems))
       if(viewableItems.length >= 1) {
         const visibleFileIndex = viewableItems[0].index
        //  console.log("visible index " + visibleFileIndex)
        this.setState({currentVisibleIndex:visibleFileIndex}) 
        const file = this.props.schedulesFiles[visibleFileIndex]
         const fileType = getFileType(file)
         console.log("file type is " + fileType)
         if (fileType == FileType.Video) {
           console.log("video file type")
         } else {
           this.schedulePlayDurationTimer(visibleFileIndex)
         }
       }
    }

    getItemLayout = (data, index) => ({
      length: deviceWidth,
      offset: deviceWidth * index,
      index,
    })

    componentDidMount(){
      this.props.getScheduleFiles()  
    }

    shouldComponentUpdate(nextProps, nextState) {
      return true
    }

    componentDidUpdate(){
      console.log("componentDidUpdate")
    }


    schedulePlayDurationTimer(file_index) {
      const file = this.props.schedulesFiles[file_index]
      const playDuration = file.play_duration_in_milliseconds
      this.timer = setTimeout(() => {
        clearTimeout(this.timer)
        this.playNextFile()
      }, playDuration);
    }


   videoEnded = () => {
     console.log("video ended")
     this.playNextFile()
   }

    playNextFile = () => {
      if(this.props.currentlyPlayingIndex == (this.props.schedulesFiles.length - 1)) {
        //last file played
        this.props.getScheduleFiles() 
        this.props.playNextFile(this.props.schedulesFiles,this.props.currentlyPlayingIndex)
        this.listRef.scrollToIndex({animated: false, index: this.props.currentlyPlayingIndex})

      } else {
        console.log("playNextFile current index " + this.props.currentlyPlayingIndex)
        this.props.playNextFile(this.props.schedulesFiles,this.props.currentlyPlayingIndex)
        console.log("playNextFile next index " + this.props.currentlyPlayingIndex)
        this.listRef.scrollToIndex({animated: true, index: this.props.currentlyPlayingIndex})
      }
    }

    _renderItem = ({item, index}) => {
      return (
        this.renderPlayer(item,index)
      );
    }

    renderPlayer(file,index) {    
        switch (getFileType(file)) {
          case FileType.Video:
            return <VideoPlayer file={file}  onEnd={this.videoEnded} currentIndex={index} currentVisibleIndex={this.state.currentVisibleIndex} />
            case FileType.Audio:
              return <AudioPlayer file={file} onEnd={this.playNextFile} />
          case FileType.Image:
            return <ImagePlayer file={file} onEnd={this.playNextFile} />
          case FileType.Pdf:
              return <PdfPlayer file={file} onEnd={this.playNextFile} />
          case FileType.WebpageContent:
            return <WebsitePlayer file={file} onEnd={this.playNextFile} />
          default:
            showToastMessage(
              I18n.t('ErrorMessage.FormatNotSupported', {
                name: getFileExtension(file).toUpperCase()
              })
            )
            this.playNextFile()
        }
    }

    render() {
      if(this.props.schedulesFiles.length > 0 ) {
          return (

              <View style={{flex:1}}>
              <StatusBar hidden={true} />
              <FlatList
                style={{flex:1}}
                bounces={false}
                removeClippedSubviews={true}
                scrollEnabled={false}
                showsHorizontalScrollIndicator={false}

                ref={el => this.listRef = el}
                horizontal={true}

                keyExtractor={(item, index) => index.toString()}

                data={this.props.schedulesFiles}
                renderItem={this._renderItem}
                onViewableItemsChanged={this.onViewableItemsChanged}
                viewabilityConfig={this.viewabilityConfig}


                getItemLayout={this.getItemLayout}
                initialNumToRender={2}
                maxToRenderPerBatch={2}
                windowSize={this.props.schedulesFiles.length}
              />
            <KeepAwake />
            </View>
          )
      }else {
        return (
          <TouchableWithoutFeedback delayLongPress={constants.REVEAL_SIDE_BAR_MENU_PRESS_DURATION} onLongPress={() => this.props.navigation.openDrawer()}>
            <View style={styles.container}>
                <NothingTpPlay/>
                <KeepAwake />
            </View>
          </TouchableWithoutFeedback>
        )
      }
    }
}


const styles = ScaledSheet.create({
    container: {
      flex:1,
      backgroundColor : 'white', 
    }
  });


//redux binding
// The function takes data from the app current state,
// and insert/links it into the props of our component.
// This function makes Redux know that this component needs to be passed a piece of the state
function mapStateToProps(state, props) {
  return {
      loading: state.scheduleReducer.loading,
      schedulesFiles: state.scheduleReducer.data,
      currentlyPlayingIndex: state.scheduleReducer.nextFileIndex,
  }
}

// Doing this merges our actions into the component’s props,
// while wrapping them in dispatch() so that they immediately dispatch an Action.
// Just by doing this, we will have access to the actions defined in out actions file (action/home.js)
function mapDispatchToProps(dispatch) {
  return bindActionCreators(Actions, dispatch);
}

//Connect everything
export default connect(mapStateToProps, mapDispatchToProps)(PlaylistPlayerScreen);
动作码

//gets called initially on app launch to get files to be played
export function getScheduleFiles(){
    return (dispatch) => {
        getOfflineNextScheduleFiles().then((files)=>{//get offline files/schedule first
            plainFiles = convertToArray(files)
            dispatch({type: SCHEDULE_REFRESHED, data:plainFiles,nextFileIndex:0});
        }).catch((error)=>{//if offline schedules is not available to play, refresh online
            triggerPlaylistsRefresh().then((files)=>{
                plainFiles = convertToArray(files)
                dispatch({type: SCHEDULE_REFRESHED, data:plainFiles,nextFileIndex:0});
            }).catch((error)=>{
                console.log("nothing to play")
                dispatch({type: PLAY_NEXT_FILE, nextFileIndex:0});
                showToastMessage(I18n.t("ErrorMessage.NoSchedulesAvailableForCurrentTimeError"))
            })
        })
    }
}

//get called from PlaylistPlayerScreen after each file played
export function playNextFile(files,filePlayedIndex){
    return (dispatch) => {
        if(filePlayedIndex < files.length-1) {
            dispatch({type: PLAY_NEXT_FILE, nextFileIndex:filePlayedIndex+1});
          }else {
            console.log("all files played")
            dispatch({type: PLAY_NEXT_FILE, nextFileIndex:0});
          }
    }
}
//在应用程序启动时最初被调用以获取要播放的文件
导出函数getScheduleFiles(){
返回(发送)=>{
getOfflineNextScheduleFiles()。然后((文件)=>{//首先获取脱机文件/计划
plainFiles=convertToArray(文件)
分派({type:SCHEDULE\u REFRESHED,data:plainFiles,nextFileIndex:0});
}).catch((错误)=>{//如果无法播放脱机时间表,请联机刷新
TriggerPlaylistResFresh()。然后((文件)=>{
plainFiles=convertToArray(文件)
分派({type:SCHEDULE\u REFRESHED,data:plainFiles,nextFileIndex:0});
}).catch((错误)=>{
console.log(“无需播放”)
分派({type:PLAY_NEXT_FILE,nextFileIndex:0});
showToastMessage(I18n.t(“ErrorMessage.NoSchedulesAvailableForCurrentTimeError”))
})
})
}
}
//播放每个文件后从播放列表屏幕调用
导出函数playNextFile(文件、文件PlayedIndex){
返回(发送)=>{
if(filePlayedIndex
我很想看看减速机内部和父组件中发生了什么。你能再给我们一些代码吗?谢谢@Murmeltier,我添加了一些代码,它调用了视频结束方法,但在reducer/redux/Wizard的某个地方出错了,是吗?是的,这个.props.playNextFile(这个.props.SchedulesFile,这个.props.CurrentPlayinIndex),currentlyPlayingIndex的值保持不变,相反,它应该增加1如果相同的方法,即通过父组件中启动/结束的计时器调用playNextFile(),它的工作方式就像charmI希望看到减速机内部和父组件中发生的事情一样。你能再给我们一些代码吗?谢谢@Murmeltier,我添加了一些代码,它调用了视频结束方法,但在reducer/redux/Wizard的某个地方出错了,是吗?是的,这个.props.playNextFile(这个.props.SchedulesFile,这个.props.CurrentPlayinIndex),currentlyPlayingIndex的值保持不变,相反,它应该增加1如果相同的方法,即通过父组件中启动/结束的计时器调用playNextFile(),它的工作方式就像一个符咒
export default class VideoPlayer extends React.Component {

  constructor() {
    super();
    this.state = {
    }
    this.videoEnded = this.videoEnded.bind(this)
  }

 videoEnded() {
    if (this.props.shouldRepeat == true) {
    } else {
      this.video.paused = true
      this.video.seek(0)
    }
    this.props.onEnd()
  }

render() {
    return (
      <Video 
        ref={ref => {
            this.video = ref;
          }}
        onError={this.videoEnded}
        minLoadRetryCount={1}
        useTextureView={true}
        controls={false} 
        style={ContainerStyle.playerTypeStyle} 
        onEnd={this.videoEnded}
        repeat={this.props.shouldRepeat} 
        playInBackground={false}
        playWhenInactive={false}
        ignoreSilentSwitch={"ignore"}

        resizeMode={this.props.file.resize_mode} 
        source={{uri:getFileAbsolutePath(this.props.file)}} 
        paused={this.props.currentIndex != this.props.currentVisibleIndex}
        />      
    )
  }

}
import { combineReducers } from 'redux';

import { SCHEDULE_REFRESHED,PLAY_NEXT_FILE } from "../actions/ScheduleActions.js" //Import the actions types constant we defined in our actions

let dataState = { data: [], loading:true };

const scheduleReducer = (state = dataState, action) => {
    switch (action.type) {
        case SCHEDULE_REFRESHED:
            state = Object.assign({}, state, { data: action.data, nextFileIndex:action.nextFileIndex });
            return state;
        case PLAY_NEXT_FILE:
            state = Object.assign({}, state, { nextFileIndex: action.nextFileIndex});
            return state;
        default:
            return state;
    }
}

// Combine all the reducers
const rootReducer = combineReducers({
    scheduleReducer
    // ,[ANOTHER REDUCER], [ANOTHER REDUCER] ....
})

export default rootReducer;
//gets called initially on app launch to get files to be played
export function getScheduleFiles(){
    return (dispatch) => {
        getOfflineNextScheduleFiles().then((files)=>{//get offline files/schedule first
            plainFiles = convertToArray(files)
            dispatch({type: SCHEDULE_REFRESHED, data:plainFiles,nextFileIndex:0});
        }).catch((error)=>{//if offline schedules is not available to play, refresh online
            triggerPlaylistsRefresh().then((files)=>{
                plainFiles = convertToArray(files)
                dispatch({type: SCHEDULE_REFRESHED, data:plainFiles,nextFileIndex:0});
            }).catch((error)=>{
                console.log("nothing to play")
                dispatch({type: PLAY_NEXT_FILE, nextFileIndex:0});
                showToastMessage(I18n.t("ErrorMessage.NoSchedulesAvailableForCurrentTimeError"))
            })
        })
    }
}

//get called from PlaylistPlayerScreen after each file played
export function playNextFile(files,filePlayedIndex){
    return (dispatch) => {
        if(filePlayedIndex < files.length-1) {
            dispatch({type: PLAY_NEXT_FILE, nextFileIndex:filePlayedIndex+1});
          }else {
            console.log("all files played")
            dispatch({type: PLAY_NEXT_FILE, nextFileIndex:0});
          }
    }
}