2017-10-06 99 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 
相關問題