关于 javascript:如何使父/package组件”触发”或调用子组件的方法?

How to make a parent/wrapper component "trigger" or call methods on children?

假设我有以下内容:

1
2
3
4
<Group>
  <Item mediaUrl="http://some/media/file.mp3" />
  <Item mediaUrl="http://another/media/file.mp3" />
</Group>

当第一个文件播放完毕后,我可以通过 props 传递的方法通知 Group

1
2
3
4
5
// Group.js
const items = React.Children.map(this.props.children, (child, i) => {
  // ...
  return React.cloneElement(child, { onEnd: this.handleEnd });
});

我想不通的是,如何让 Group 触发 Item 中的方法,而不使用 Redux 之类的东西? (我试图让这些组件尽可能简单和纯粹)


你不能也不应该。但在少数情况下,比如设置焦点或触发一些不会真正改变状态的东西,它可能是需要的。这个问题的关键是还没有创建DOM。反应文档中有一个例子。

1
2
3
4
5
6
render: function() {
  return <TextInput ref={(c) => this._input = c} />;
},
componentDidMount: function() {
  this._input.focus();
},

所以在这里设置 ref 属性,然后在挂载 dom 后设置焦点。在其他情况下,我会建议使用从父组件到子组件的传递属性来完成所有操作。

更多帮助:

  • React.js - 访问组件方法
  • 在 React 子组件上调用方法
  • https://discuss.reactjs.org/t/is-it-bad-practice-to-call-child-component-methods-from-parent/1821

我假设您正在尝试播放下一首歌曲?我可能尝试解决这个问题的方法是传递另一个道具,可能是一个布尔值,它决定了歌曲是否在渲染时播放。父级将跟踪上次播放的歌曲、正在播放的歌曲或将在其状态下播放的歌曲。当您的歌曲结束事件触发时,父级将调用 setState 并更新每个状态值并相应地重新渲染自身及其子级。

组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
class Group extends React.Component {
  constructor(props) {
    super(props)
  }
  state = {
    // Initial state. Be careful pressing the back button on the first song :p (Obviously improve this)
    previous: -1,
    current: 0,
    next: 1
  }
  handleEnd = () => {
    // Perform all sorts of condition checks for first song, last song, etc. and code accordingly. This is simplistic, proceeding in linear fashion. You can extrapolate this into other methods for selection of any song, back or forward button, etc.
    if (...) {
      this.setstate({
        previous: previous += 1,
        current: current += 1,
        next: next +=1
      });
    }
  }
  render () {
    let songs = this.props.songs;

    let songList = songs.map((song) => {
      if (song.index === this.state.current) {
        return (
          <Item playing=1 onEnd={this.handleEnd} mediaUrl={song.url} />
        );
      }
      else {
        return (
          <Item playing=0 onEnd={this.handleEnd} mediaUrl={song.url} />
        );
      };
    }

    return (
     
        {songList}
     
    );
  }
}

class Item extends React.Component {
  constructor(props) {
    super(props)
  }
  componentDidMount = () => {
    if (this.props.playing === 1) {
      this.play();
    };
  }
  play = () => {
    // Play the music from the url or whatever.
  }
  render () {
   
      // Some graphical representation of song.
   
  }
}

创建新的播放列表组件:

1
2
3
4
5
someSongs = [...];
ReactDOM.render(
  <Group songs={someSongs} />,
  document.getElementById('...')
);

想出了一个不涉及黑客或参考的解决方案,并认为我会分享。

我的 Group 组件有以下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  componentDidMount() {
    this.setState({
      playlist: React.Children.map(this.props.children, t => t.props.id),
    });
  }

  render() {
    if (!this.props.children) {
      console.error('Warning: TrackGroup must contain audio tracks');
      return null;
    }

    const tracks = React.Children.map(this.props.children, (child, i) => {
      return React.cloneElement(child, {
        onTrackEnd: this.trackEnded,
        playNext: this.state.playNext,
      });
    });

    return {tracks}
  }

并且当每个 Item\\'s (track) componentWillReceiveProps 触发时,我检查 nextProps.playNext 以查看它是否与 this.props.id 匹配。如果是,则音频调用 this.clickedPlay()(就像用户单击了播放按钮一样)。很有魅力。