react调用子方法的不同方式

IT技术 javascript reactjs
2021-04-29 18:53:42

React 说我们不应该refs在可能的情况下使用,我注意到你不能对 refs 使用浅层渲染测试,所以我试图在可能的情况下删除 refs。我有一个这样的子组件:

class Child extends React.Component {
    play = () => {
        //play the media
    },
    pause = () => {
        //pause the media
    },
    setMedia = (newMedia) => {
        //set the new media
    }
}

然后我有一个需要调用这些方法的父组件。对于setMedia我可以只使用propscomponentWillReceivePropssetMedia当新props进入孩子时调用

使用playpause功能我无法做到这一点。

Ben Alpert 回复了这篇文章并说:

一般来说,数据应该通过 props 沿着树向下传递。对此有一些例外(例如调用.focus()或触发并没有真正“改变”状态的一次性动画)但是任何时候你暴露一个名为“set”的方法,props通常是更好的选择。尽量让内部输入组件担心它的大小和外观,这样它的祖先就不会担心了。

哪个是调用子函数的最佳方式?

  1. play()pause()方法可以从 refs 调用,因为它们不会像focus()其他有参数的函数一样使用 props改变状态
  2. 通过传入方法名称来调用子函数,尽管这看起来很笨拙而且要复杂得多:

    class Child extends React.Component {
        play = () => {
            //play the media
        },
        pause = () => {
            //pause the media
        },
        setMedia = (newMedia) => {
            //set the new media
        },
        _callFunctions = (functions) => {
            if (!functions.length) {
                return;
            }
    
            //call each new function
            functions.forEach((func) => this[func]());
    
            //Empty the functions as they have been called
            this.props.updateFunctions({functions: []});
        } 
        componentWillReceiveProps(nextProps) {
            this._callFunctions(nextProps.functions);
        }
    }
    
    class Parent extends React.Component {
        updateFunctions = (newFunctions) => this.setState({functions: newFunctions});
        differentPlayMethod = () => {
            //...Do other stuff
            this.updateFunctions("play");
        }
        render() {
            return (
                <Child updateFunctions={this.updateFunctions}/>
            );
        }
    }
    
  3. 在子组件中执行此操作:this.props.updateFunctions({play: this.play}); 这样做的问题是我们将一个方法暴露(复制)给另一个不应该真正了解它的组件......

哪种方法最好?

我目前正在使用方法 2,但我并不喜欢它。

为了覆盖子函数,我也做了类似上面的事情。我应该只使用 refs 吗?

2个回答

与其调用子函数,不如尝试从父函数向下传递数据和函数。除了您的组件,您还可以导出提供必要状态/功能的包装器或高阶函数。

let withMedia = Wrapped => {
  return class extends React.Component {
    state = { playing: false }
    play() { ... }
    render() {
      return (
        <Wrapped 
          {...this.state} 
          {...this.props} 
          play={this.play}
        />
      )
    }
  }
}

然后在您的父组件中:

import { Media, withMedia } from 'your-library'

let Parent = props =>
  <div>
    <button onClick={props.play}>Play</button>
    <Media playing={props.playing} />
  </div>

export default withMedia(Parent)

尽可能保持状态本地化,但不要将其分散到多个组件上。如果您需要了解当前是否在父级和子级中播放的信息,请将状态保留在父级中。

这给你留下了一个更干净的状态树和props:

class Child extends React.Component {
  render() {
    return (
      <div>
        <button onClick={this.props.togglePlay}>Child: Play/Pause</button>
        <p>Playing: {this.props.playing ? 'Yes' : 'No'}</p>
      </div>
    );
  }
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    
    this.togglePlay = this.togglePlay.bind(this);
    
    this.state = {
      playing: false
    };
  }
  
  togglePlay() {
    this.setState({
      playing: !this.state.playing
    });
  }
  
  render() {
    return (
      <div>
        <button onClick={this.togglePlay}>Parent: Play/Pause</button>
        <Child togglePlay={this.togglePlay} playing={this.state.playing} />
      </div>
    );
  }
}

ReactDOM.render(
  <Parent />,
  document.getElementById('app')
);
<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='app'></div>