2017-10-06 52 views
0

我有两个组件,父和子。父母跟踪音频播放器组件(孩子是玩家)以及玩家正在玩什么片段,例如,段1可能是第一个34秒,然后是第二段,直到215秒,等等。在调用父函数设置父状态后,React Child组件会挂载然后卸载

我的父组件将呈现播放器组件并将绑定函数传递给播放器,以便播放器可以用当前时间玩家可以确定哪个段应该突出显示。

的问题是:(1)(大问题)一旦点击播放按钮,它扮演,或者用户跳过,超出第一段破则父更新的状态,但播放器被卸载,造成MediaElement被删除; (2)初始加载页面时(小问题),玩家卸载,然后是父装载,然后玩家卸载并重新安装。我相信他们是相关的。

父:

import React from 'react' 
import shortid from 'shortid' 

import Frame from '../../layout/Frame' 
import Box from '../../layout/Box' 
import Flex from '../../layout/Flex' 
import G1 from '../../layout/G1' 

import Player from '../../parts/Player' 

import BriefingTitle from './BriefingTitle' 

import {assoc, lensPath, set, view} from 'ramda' 
import {createMarkup} from '../../../lib/tools' 

class Briefing extends React.Component { 
    constructor({briefing}) { 
    super() 

    const segments = briefing.segments.map(assoc('playing', false)) 
    console.log('segments:', segments) 
    this.state = { 
     briefing, 
     segments 
    } 
    this.parentMonitor = this.updateSegments.bind(this) 
    } 

    updateSegments(time) { 
    console.log('time:', time) 
    const firstPlayingLens = lensPath([0, 'playing']) 
    if (time > 36 && !view(firstPlayingLens, this.state.segments)) { 
     this.setState(set(firstPlayingLens, true, this.state.segments)) 
    } 
    } 

    componentDidMount() { 
    console.log('Briefing mounted') 
    } 

    componentWillUnmount() { 
    console.log('Briefing will unmount') 
    } 

    render() { 
    const {briefing, segments} = this.state 
    return (
     <Frame pb={['0px', 3]}> 
     <G1> 
      <Flex pt={[2, 3]} direction={['column', 'row']}> 
      <Box mt={[2, 'm']} mr={2} shrink={0} grow={2} order={[2, 1]}> 
       <BriefingTitle><span dangerouslySetInnerHTML={createMarkup(briefing.title)} /></BriefingTitle> 

       <Box mt={0} pt={0} bt> 
       <Player key={'briefing_'+briefing.id} url={briefing.audioFile} type="audio/mp3" duration={briefing.duration} parentMonitor={this.parentMonitor}>Play Full Episode</Player> 
       </Box> 
       <Box mt={0} pt={0} bt> 
       {briefing.segments.map(s => s.playing ? <p><strong>{s.title}</strong></p> : <p>{s.title}</p>)} 
       </Box> 
      </Box> 
      </Flex> 
     </G1> 
     </Frame> 
    ) 
    } 
} 

export default Briefing 

球员:

import React from 'react' 
import styled from 'styled-components' 

import Flex from '../../layout/Flex' 
import Box from '../../layout/Box' 

import 'mediaelement' 
import 'mediaelement/build/mediaelementplayer.min.css' 
import 'mediaelement/build/mediaelement-flash-video.swf' 
import 'mediaelement-plugins/dist/skip-back/skip-back.min.js' 
import 'mediaelement-plugins/dist/skip-back/skip-back.css' 

import {rem} from '../../../lib/tools' 
import {type} from '../../../designSystem' 

const StyledSpan = styled.span` 
    font-family: ${type.family.default}; 
    font-size: ${rem(type.size.s0)}; 
    font-weight: ${type.weight.bold}; 
    line-height: ${type.lineHeight.meta}; 
` 

class Player extends React.Component { 
    constructor(props, { 
    inverse = props.inverse ? true : false 
    }) { 
    super() 
    this.state = { 
     inverse, 
     children: props.children, 
     player: null 
    } 
    } 

    monitor(media) { 
    this.props.parentMonitor(media.getCurrentTime()) 
    setTimeout(this.playing.bind(this), 200) 
    } 

    playing() { 
    this.monitor(this.state.player) 
    } 

    success(media, node, instance) { 
    // successfully loaded! 
    const playEvent = e => this.playing() 
    media.addEventListener('playing', playEvent) 
    media.removeEventListener('pause', playEvent) 
    media.removeEventListener('ended', playEvent) 
    } 

    error(media) { 
    // failed to load 
    } 

    componentDidMount() { 
    console.log('Player mounted') 
    const {MediaElementPlayer} = global 
    if (MediaElementPlayer) { 
     const options = { 
     features: ['skipback'], 
     useDefaultControls: true, 
     pluginPath: './build/static/media/', 
     skipBackInterval: 31, 
     skipBackText: 'Rewind 30 seconds', 
     success: (media, node, instance) => this.success(media, node, instance), 
     error: (media, node) => this.error(media, node) 
     } 
     this.setState({player: new MediaElementPlayer('player_'+this.props.key, options)}) 
    } 
    } 

    componentWillUnmount() { 
    console.log('Player will unmount') 
    if (this.state.player) { 
     this.state.player.remove() 
     this.setState({player: null}) 
    } 
    } 

    shouldComponentUpdate() { 
    return false 
    } 

    render() { 
    return (
     <Flex justify={this.state.children ? 'space-between' : ''} align="center"> 
     <Flex align="center"> 
      <audio id={'player_'+this.props.key} width={this.props.width || 400}> 
      <source src={this.props.url} type={this.props.type} /> 
      </audio> 
     </Flex> 
     </Flex> 
    ) 
    } 
} 

export default Player 

我使用MediaElement并作出反应15.5.4。

+0

的通报组件尝试播放器一键设置是这样的:'<玩家键=“MyPlayer”... />' – Hoyen

+0

Thanks @Hoyen。我添加了它,它可能已经解决了这个小问题,但主要的问题仍然是玩家卸载。我会用你的建议更新这个问题。 – Joshua

+1

当你setState()时,它会导致整个组件重新渲染。这可以说是为什么它正在卸载。渲染组件时是否需要'segments'?如果不是,请实现一个'shouldComponentUpdate()'函数,如果不希望它重新渲染,则返回false。 – Hoyen

回答

0

@ Hoyen帮助找出重新渲染是由父母的状态变化引起的,我想我需要将父母的statestate分开。我将这些片段放在他们自己的孩子班级中,并在玩家更新时间时从父母中调用它们。

注意(在父级中)调用段细分子this.refs.segments.updateSegmentsref属性的父级呈现段,<Segments ref="segments" key={"bSegments"+this.state.briefing.id} segments={segments}></Segments>可以调用子组件。

家长:

import React from 'react' 
import shortid from 'shortid' 

import Frame from '../../layout/Frame' 
import Box from '../../layout/Box' 
import Flex from '../../layout/Flex' 
import G1 from '../../layout/G1' 

import Player from '../../parts/Player' 
import Segments from '../../parts/Player/Segments' 

import BriefingTitle from './BriefingTitle' 

import {assoc, lensPath, set, view} from 'ramda' 
import {createMarkup} from '../../../lib/tools' 

class Briefing extends React.Component { 
    constructor({briefing}) { 
    super() 

    const segments = briefing.segments.map(assoc('playing', false)) 
    console.log('segments:', segments) 
    this.state = { 
     briefing, 
     segments 
    } 
    this.parentMonitor = this.updateSegments.bind(this) 
    } 

    updateSegments(time) { 
    this.refs.segments.updateSegments(time) 
    } 

    componentDidMount() { 
    console.log('Briefing mounted') 
    } 

    componentWillUnmount() { 
    console.log('Briefing will unmount') 
    } 

    render() { 
    const {briefing, segments} = this.state 
    console.log('render Briefing') 
    return (
     <Frame pb={['0px', 3]}> 
     <G1> 
      <Flex pt={[2, 3]} direction={['column', 'row']}> 
      <Box mt={[2, 'm']} mr={2} shrink={0} grow={2} order={[2, 1]}> 
       <BriefingTitle><span dangerouslySetInnerHTML={createMarkup(briefing.title)} /></BriefingTitle> 

       <Box mt={0} pt={0} bt> 
       <Player key={'briefing_'+briefing.id} url={briefing.audioFile} type="audio/mp3" duration={briefing.duration} parentMonitor={this.parentMonitor}>Play Full Episode</Player> 
       </Box> 
       <Segments ref="segments" key={"bSegments"+this.state.briefing.id} segments={segments}></Segments> 
      </Box> 
      </Flex> 
     </G1> 
     </Frame> 
    ) 
    } 
} 

export default Briefing 

球员:

import React from 'react' 
import styled from 'styled-components' 

import Flex from '../../layout/Flex' 
import Box from '../../layout/Box' 

import 'mediaelement' 
import 'mediaelement/build/mediaelementplayer.min.css' 
import 'mediaelement/build/mediaelement-flash-video.swf' 
import 'mediaelement-plugins/dist/skip-back/skip-back.min.js' 
import 'mediaelement-plugins/dist/skip-back/skip-back.css' 

import {rem} from '../../../lib/tools' 
import {type} from '../../../designSystem' 

const StyledSpan = styled.span` 
    font-family: ${type.family.default}; 
    font-size: ${rem(type.size.s0)}; 
    font-weight: ${type.weight.bold}; 
    line-height: ${type.lineHeight.meta}; 
` 

class Player extends React.Component { 
    constructor(props, { 
    inverse = props.inverse ? true : false 
    }) { 
    super() 
    this.state = { 
     inverse, 
     children: props.children, 
     player: null 
    } 
    } 

    monitor(media) { 
    this.props.parentMonitor(media.getCurrentTime()) 
    setTimeout(this.playing.bind(this), 200) 
    } 

    playing() { 
    this.monitor(this.state.player) 
    } 

    success(media, node, instance) { 
    // successfully loaded! 
    const playEvent = e => this.playing() 
    media.addEventListener('playing', playEvent) 
    media.removeEventListener('pause', playEvent) 
    media.removeEventListener('ended', playEvent) 
    } 

    error(media) { 
    // failed to load 
    } 

    componentDidMount() { 
    console.log('Player mounted') 
    const {MediaElementPlayer} = global 
    if (MediaElementPlayer) { 
     const options = { 
     features: ['skipback'], 
     useDefaultControls: true, 
     pluginPath: './build/static/media/', 
     skipBackInterval: 31, 
     skipBackText: 'Rewind 30 seconds', 
     success: (media, node, instance) => this.success(media, node, instance), 
     error: (media, node) => this.error(media, node) 
     } 
     this.setState({player: new MediaElementPlayer('player_'+this.props.key, options)}) 
    } 
    } 

    componentWillUnmount() { 
    console.log('Player will unmount') 
    if (this.state.player) { 
     this.state.player.remove() 
     this.setState({player: null}) 
    } 
    } 

    shouldComponentUpdate() { 
    return false 
    } 

    render() { 
    console.log('render player') 
    return (
     <Flex justify={this.state.children ? 'space-between' : ''} align="center"> 
     <Flex align="center"> 
      <audio id={'player_'+this.props.key} width={this.props.width || 400}> 
      <source src={this.props.url} type={this.props.type} /> 
      </audio> 
     </Flex> 
     </Flex> 
    ) 
    } 
} 

export default Player 

段:

import React from 'react' 

import Box from '../../layout/Box' 

import {lensPath, set, view} from 'ramda' 

class Segments extends React.Component { 
    constructor(props) { 
    super() 

    this.state = { 
     segments: props.segments 
    } 
    } 

    updateSegments(time) { 
    console.log('time:', time) 
    const firstPlayingLens = lensPath([0, 'playing']) 
    if (time > 36 && !view(firstPlayingLens, this.state.segments)) { 
     const modifiedSegments = set(firstPlayingLens, true, this.state.segments) 
     console.log('modifiedSegments:', modifiedSegments) 
     this.setState({segments: modifiedSegments}) 
    } 
    } 

    render() { 
    console.log('render Segments') 
    return (
     <Box mt={0} pt={0} bt> 
     {this.state.segments.map(s => s.playing ? <p><strong>{s.title}</strong></p> : <p>{s.title}</p>)} 
     </Box> 
    ) 
    } 
} 

export default Segments 
相关问题