You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

React中如何使用Refs?获取点击元素Ref及进度栏显示问题

如何在React中获取当前点击的故事元素Ref并更新进度栏名称?

我是React新手,正在开发一款故事播放器(类似音频播放器,用于播放故事),目前遇到一个问题:在控制台打印Ref时,总是得到相同的元素Ref,想获取当前点击元素的Ref,以便在进度栏中渲染对应的故事名称。

我的代码:

render() { 
  const storiesList = this.props.stories; 
  return( 
    <div> 
      { storiesList.map((story, k) => { 
        return( 
          <div ref={this.myRef} key={k} className="story" onClick={() => this.playAudio(story.playing_url)} onChange={console.log(this.myRef.current)} > 
            <img src={story.image_url} className="story-img" alt="story" /> 
            <div className="story-play"> 
              <div className="story-play-inner"> 
                { this.state.playing_story === story.playing_url && this.state.playing ? <span>&#124; &#124;</span> : <span>&#9654;</span> } 
              </div> 
            </div> 
            <p className="story-name"> {story.name} </p> 
          </div> 
        ) 
      })} 
      <div className="progress-bar" > 
        <div className="playBtn"> 
          <span>&#9654;</span> 
        </div> 
        <div className="progress-container"> 
          <div className="progress-title"> 
            <span>Story name</span> 
          </div> 
          <div className="timeline"> 
            <div className="playhead"></div> 
          </div> 
          <div className="progress-time"> 
            <span className="progress-time-played">4:55 </span> 
            <span className="progress-time-total">/ 8:15</span> 
          </div> 
        </div> 
      </div> 
    </div> 
  ) 
} 

问题原因

你现在用的是单个myRef,当循环渲染故事项时,所有项都会把自身的DOM元素赋值给同一个myRef变量,最终只会保留最后一个渲染元素的Ref,所以打印出来的总是同一个结果。

解决方案

根据你的需求(更新进度栏的故事名称),有两种思路,推荐第二种更符合React的状态驱动理念:

方案1:使用Ref数组(如果确实需要操作DOM元素)

如果你需要对当前点击的DOM元素做一些操作(比如添加样式、获取位置等),可以用Ref数组来存储每个故事项的Ref:

  1. 初始化Ref数组:在组件的构造函数里创建一个空数组来保存所有故事项的Ref
constructor(props) {
  super(props);
  this.storyRefs = []; // 存储每个故事项的Ref
  this.state = {
    playing_story: '',
    playing: false,
    currentStoryName: '' // 新增状态,保存当前播放的故事名称
  };
}
  1. 给每个故事项分配Ref:在循环渲染时,把每个元素的Ref存入数组对应索引的位置
{storiesList.map((story, k) => {
  return(
    <div 
      ref={el => this.storyRefs[k] = el} // 将当前元素Ref存入数组
      key={k} 
      className="story" 
      onClick={() => this.playAudio(story, k)} // 传递story对象和索引
    >
      {/* 原有内容不变 */}
    </div>
  )
})}
  1. 更新播放状态并获取当前Ref:修改playAudio方法,通过索引拿到对应的Ref,同时更新状态里的故事名称
playAudio(story, index) {
  const currentElementRef = this.storyRefs[index];
  console.log('当前点击的元素Ref:', currentElementRef);
  
  // 更新播放状态和当前故事名称
  this.setState({
    playing_story: story.playing_url,
    playing: true,
    currentStoryName: story.name
  });
  
  // 这里添加你的音频播放逻辑
}
  1. 在进度栏渲染故事名称:把固定的"Story name"替换为状态里的当前故事名称
<div className="progress-title">
  <span>{this.state.currentStoryName || 'Story name'}</span>
</div>

方案2:直接利用点击事件传参(更简单,推荐)

其实你根本不需要获取Ref,因为点击事件触发时已经能拿到当前的story对象,直接把故事名称存入状态即可,完全符合React的状态驱动逻辑:

  1. 修改点击事件传参:直接把当前的story对象传给playAudio
{storiesList.map((story, k) => {
  return(
    <div 
      key={k} 
      className="story" 
      onClick={() => this.playAudio(story)} // 直接传递story对象
    >
      {/* 原有内容不变 */}
    </div>
  )
})}
  1. 更新播放状态:在playAudio里直接使用story对象的属性
playAudio(story) {
  console.log('当前播放的故事:', story.name);
  
  this.setState({
    playing_story: story.playing_url,
    playing: true,
    currentStoryName: story.name
  });
  
  // 这里添加你的音频播放逻辑
}
  1. 在进度栏渲染故事名称:和方案1一样,替换为状态里的名称
<div className="progress-title">
  <span>{this.state.currentStoryName || 'Story name'}</span>
</div>

内容的提问来源于stack exchange,提问作者Dan Cristian

火山引擎 最新活动