2017-10-06 3 views
0

親と子の2つのコンポーネントがあります。親は、オーディオプレーヤーコンポーネント(子供はプレーヤ)およびプレーヤがどのセグメントをプレイしているかを記録する。セグメント1は最初の34秒、次のセグメントは215秒まで続きます。React子コンポーネントは、親の状態を設定するためにparent関数を呼び出した後にマウント解除され、アンマウントされます。

親コンポーネントはPlayerコンポーネントをレンダリングし、バインドされた関数をPlayerに渡すので、Playerは現在の時刻で親を更新できますプレイヤーは、親がどのセグメントをハイライト表示するべきかを把握できるようにします。は、(1)(大きな問題)を1回ボタンを果たしていることがある

問題がクリックされ、それが果たしている、またはユーザースキップし、最初のセグメントを超えて、親の更新の状態を破るが、プレイヤーが原因、アンマウントされています削除されるMediaElement。 (2)(小さな問題)最初にページを読み込んだとき、Playerはアンマウントし、次に親マウントが行われ、続いてPlayerがアンマウントされ、再度マウントされます。私は彼らが関係していると思う。

親:

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

ありがとう@Hoyen。私はそれを追加したが、それは軽度の問題を解決したかもしれないが、主要なプレーヤー、アンマウントされているプレーヤーが残っている。あなたの提案で質問を更新します。 – Joshua

+1

'setState()'を実行すると、コンポーネント全体が再描画されます。それは、なぜそれがアンマウントしているのか、ということです。コンポーネントをレンダリングするときに 'segments'が必要ですか?そうでなければ、 'shouldComponentUpdate()'関数を実装し、再描画したくない場合はfalseを返します。 – Hoyen

答えて

0

@ Hoyenの助けを借りて、再レンダリングが親の状態変化によって引き起こされたことを理解すると、親のstateをセグメントのstateから分離する必要があることがわかりました。私はセグメントを自分の子クラスに入れて、プレーヤーの時間が更新されたときに親から呼び出す。

セグメントの親レンダリングでセグメント子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 
関連する問題