将React道具桥接到JS只有setter

时间:2017-03-02 07:56:44

标签: javascript reactjs

有时,人们可能想要使用prop来设置一个只能通过JS设置的元素属性(因此,通过React无法控制)。

一个示例(和我的用例)使用paused属性来定义视频是否应该播放(我的目标是分离控件和视频的可视化)。

以下是我目前的(简化)实施:

class Video extends Component {
  constructor() {
    super();
    this.videoElRef = this.videoElRef.bind(this);
  }
  // Called when the video element is mounted.
  videoElRef(ref) {
    this.videoEl = ref;
    if (this.videoEl) {
      this.updateVideoProp();
    }
  }
  // Update JS only property from the Component props.
  updateVideoProp() {
    const { paused } = this.props;
    const videoEl = this.videoEl;
    // Pause or play the video depending on the `paused` property.
    if (paused && !videoEl.paused) {
      videoEl.pause();
    } else if (!paused && videoEl.paused) {
      videoEl.play();
    }
  }
  render() {
    if (this.videoEl) {
      this.updateVideoProp();
    }
    return (
      <video ref={this.videoElRef}>
        { this.props.children }
      </video>
    );
  }
}

const Component = React.Component;

class Video extends Component {
  constructor() {
    super();
    this.videoElRef = this.videoElRef.bind(this);
  }
  // Called when the video element is mounted.
  videoElRef(ref) {
    this.videoEl = ref;
    if (this.videoEl) {
      this.updateVideoProp();
    }
  }
  // Update JS only property from the Component props.
  updateVideoProp() {
    const { paused } = this.props;
    const videoEl = this.videoEl;
    // Pause or play the video depending on the `paused` property.
    if (paused && !videoEl.paused) {
      videoEl.pause();
    } else if (!paused && videoEl.paused) {
      videoEl.play();
    }
  }
  render() {
    if (this.videoEl) {
      this.updateVideoProp();
    }
    return (
      <video ref={this.videoElRef}>
        { this.props.children }
      </video>
    );
  }
}

class App extends Component {
  constructor() {
    super();
    this.state = { paused: true };
    this.onClick = this.onClick.bind(this);
  }
  onClick(){
    this.setState(state => ({ ...state, paused: !this.state.paused }));
  }
  render() {
    return (
      <div id="app">
        <Video paused={this.state.paused}>
          <source
            src="http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_480p_h264.mov"
            type="video/mp4"
          />
        </Video>
        <p><button onClick={this.onClick}>{this.state.paused?'play':'pause'}</button></p>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
video {
  max-height: 80%;
  max-width: 80%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

工作正常。但是,我发现它非常hacky。例如。修改paused属性实际上并不需要重新呈现组件,并且updateVideoProp方法被称为render触发器的副作用。

我找不到合适的模式来实现这一目标。还有更好的办法吗?

1 个答案:

答案 0 :(得分:1)

我认为updateVideoProp方法的内容实际上属于componentDidUpdate方法,或者如果要在调用render之前更改属性,请使用componentWillRecieveProps方法。