React.js奇怪的行为

时间:2020-08-18 11:07:58

标签: javascript reactjs jsx

考虑这个简单的react组件以显示列表。

class List extends React.Component{
  constructor(props){
      super(props)
      this.data = ["abvjx", "sda", "fdsaf"]
  }   
  getRows(){      
      return this.data.map((el, index)=>{
          return (
            <li>
              {el}
              <button onClick={()=>{alert(index)}}>Click
              </button>
            </li>
          );
      })
  }
  getRows2(){
      let ret = []
      let i = 0
      for (const row of this.data){
          ret.push(
          <li>{row, i}
            <button onClick={()=>{alert(i)}}>Click</button>
          </li>);
          i++;
      }
      return ret
  }
  render(){
      return (
          this.getRows2()
      )
  }
}

ReactDOM.render(<List />, document.getElementById("container"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="container"></div>

getRows()返回一个jsx元素数组,显示数据数组中的元素及其索引,每行还具有一个按钮,单击该按钮将显示一个弹出窗口,此弹出窗口显示该行的索引。 / p>

getRows2()的功能相同,只是这次我使用for而不是Array.map循环。

Both show the same result

但是当我使用getRows2()时单击任何按钮时,弹出窗口始终显示3,而getRows()在弹出窗口中显示正确的索引

3 个答案:

答案 0 :(得分:1)

很简单,您基本上在其最后一个索引上使用i

这里:

let i = 0
for (let row of this.data){
    ret.push(
    <li>{row, i}
      <button onClick={()=>{alert(i)}}>Click</button>
    </li>);
    i++;
}
// Since you've been incrementing i within the loop.. any call after the loop to use i is when i=3

这里发生的是您向onClick注册了一个alert(i)事件。但是我独立于循环中的每个项目。 i在循环外部定义。因此,在执行click事件时,它是在呈现按钮之后,此时i已经在i = 3处了……请记住,您在循环的每个项目上都对其进行了递增。因此它一直显示3。


您可以使用的一种方法是尝试并使用data-*属性存储i。您可以按照以下方式进行操作:

class List extends React.Component{
  constructor(props){
      super(props)
      this.data = ["abvjx", "sda", "fdsaf"]
  }   
  getRows(){      
      return this.data.map((el, index)=>{
          return (
            <li>
              {el}
              <button onClick={()=>{alert(index)}}>Click
              </button>
            </li>
          );
      })
  }
  getRows2(){
      let ret = []
      let i = 0
      for (const row of this.data){
          ret.push(
          <li>{row, i}
              <button data-i={i} onClick={
                (e) => alert(e.target.getAttribute("data-i"))
              }
            >Click</button>
          </li>);
          i++;
      }
      return ret
  }
  render(){
      return (
          this.getRows2()
      )
  }
}

ReactDOM.render(<List />, document.getElementById("container"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="container"></div>

答案 1 :(得分:0)

发生这种情况是在调用i函数之前更新alert的值。

这是称为关闭的概念。

用MDN话

闭包是捆绑在一起(封闭)的函数和对其周围状态(词汇环境)的引用的组合。换句话说,闭包使您可以从内部函数访问外部函数的作用域。在JavaScript中,每次创建函数时都会在函数创建时创建闭包。

MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

Eric Elliot 给出了一个很好的解释:https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36

答案 2 :(得分:-1)

You can't push a JSX element inside an array。 这段代码是完全错误的:

getRows2(){ 
   let ret = []
   let i = 0 
   for (const row of this.data){ ret.push(<li>{row, i}<button onClick={()=>{alert(i)}}>Click</button></li>) i++; } 
  return ret 
} 

您需要这样清除代码:

render(){
   return (
      <ul>
         {this.data.map((el, index)=>{
             return (<li>{el}<button onClick={()=>{alert(index)}}>Click</button></li>) })
         }
      </ul>
   )
}