首页 > 解决方案 > 如何与 React 中的子组件通信?

问题描述

我有一个父组件VideoPlayer,它有条件地呈现许多不同的视频播放器提供商之一(想想 videoJS、JW 播放器等) - 所以VideoJSPlayerJWPlayer等等。

父组件监视视频何时播放完毕并需要让子组件知道是时候播放新视频了。每个不同的视频提供者都有不同的 JS 库/API,我可能会引用它们。父组件还呈现共享组件,如播放按钮、跳过按钮等。

我怎样才能最容易地让父组件保持类似的功能并确保子组件只处理特定于库的操作?

例如,如果单击共享的跳过按钮,我应该如何从父级调用它?我目前正在使用postMessage(),但感觉不太对劲。

这是一些最小的代码。实际上,父组件维护和共享的东西不止一件事。

class Player extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      props.videos,
      activeIndex: 0,
      activeVideo: props.videos[0]
    }
  }

  loadNextVideo() {
    // how to communicate to VideoJSPlayer and JWPlayer?
  }

  render() {
    const playerCode = this.props.playerCode
    return (
      <div>
        {
          playerCode === 'videojs' ?
            <VideoJSPlayer/> :
            playerCode === 'jwplayer' ? 
            <JWPlayer/> : null
        }
        <SkipButton
          activeVideo={this.state.activeVideo}
          onClick={() => this.loadNextVideo()}/>
    )
  }
}

标签: javascriptreactjs

解决方案


我会开始指出一些糟糕的模式:

  • 从道具(props.videos)派生的状态;

大多数时候你不应该重复道具来声明;

  • 从其他状态派生的状态(activeVideo);

activeVideo不是必要状态。activeIndex它是从and派生的计算值props.videos。是冗余状态,只需要保存当前视频的引用即可;

还有一些小技巧;

  • constructor几年不需要,您可以直接在外面声明您的州;
  • 将您的方法声明为箭头函数是一种实用且更简洁的绑定方式this

鉴于此,您loadNextVideo将更新到下一个索引。VideoJSPlayer并且JWPlayer应该作为道具接收,activeVideo并且loadNextVideo应该在视频播放完毕时在这些组件中调用;

class Player extends React.Component {

  state = {
    activeIndex = 0
  }

  loadNextVideo = () => {
    // you might add some validation if it's the last index
    if (this.props.videos.length - 1 === this.state.activeIndex) return  
    this.setState(({ activeIndex }) => ({ activeIndex: activeIndex + 1 }))
  }

  render() {
    const { activeIndex } = this.state
    const activeVideo = this.props.videos[activeIndex]
    const playerCode = this.props.playerCode
    return (
      <div>
        {
          playerCode === 'videojs' ?
            <VideoJSPlayer activeVideo={ activeVideo } loadNextVideo={ this.loadNextVideo } /> :
            playerCode === 'jwplayer' ? 
            <JWPlayer activeVideo={ activeVideo } loadNextVideo={ this.loadNextVideo } /> : null
        }
        <SkipButton
          activeVideo={ this.state.activeVideo }
          onClick={ this.loadNextVideo }/>
      </div>
    )
  }
}

推荐阅读