React js:与其他元素相比,输入元素的呈现方式不同

IT技术 reactjs
2021-04-01 06:59:29

我制作了一个简单的应用程序(在 react js 中),它有两个列表,您可以向该列表添加元素。一个是输入元素列表,另一个是跨度列表。

添加新元素后,span 列表呈现完美,但输入列表呈现不同。

这就是我的react课的样子

var App = React.createClass({
  getInitialState: function(){
    return {
      'app': {
        'data': this.props.data,
        'data2': this.props.data2
      }
    };
  },
  onclick: function(){
    var dat = this.state.app.data;
    var val = this.refs.input.getDOMNode().value;

    dat.splice(0, 0, val);

    var dat2 = this.state.app.data2;
    dat2.splice(0, 0, val);

    this.setState({'app': {
      'data': dat,
      'data2': dat2
    }});

  },
  renderElement: function(i){
    return(
      <input defaultValue={i} />
    );
  },
  renderElement2: function(i){
    return(
      <span>{i}</span>
    );
  },
  render: function(){
    var self = this;
    return(
      <div>
        <input type="text" ref="input" placeholder="Enter a value"/>
        <input type="button" value="Add value" onClick={this.onclick} />
        <div className="col2">
          {this.state.app.data.map(function(page, i){
              return(
                <div key={i}>{self.renderElement(page)}</div>
              );
          })}
        </div>
        <div className="col2">
          {this.state.app.data2.map(function(page, i){
              return(
                <div key={i}>{self.renderElement2(page)}</div>
              );
          })}
        </div>
      </div>
    );
  }
});

这是应用程序的呈现方式

var data =  [1,2,3];
var data2 =  [1,2,3];

React.render(<App data={data} data2={data2}/>, document.getElementById("main"));

这是演示

My test case:
I add 55 and expect the results to look like this 
55
1
2
3
55
1
2
3

But I get
1
2
3
3
55
1
2
3

我在这里错过了一些基本的东西吗?

2个回答

第一次渲染看起来(大致)是这样的:

<div>
 <div key={0}><input defaultValue="1" /></div>
 <div key={1}><input defaultValue="2" /></div>
 <div key={2}><input defaultValue="3" /></div>
</div>
<div>
 <div key={0}><span>1</span></div>
 <div key={1}><span>2</span></div>
 <div key={2}><span>3</span></div>
</div>

酷,没问题。然后假设我将“foo”放入框中并单击“添加值”。通过在第一项之前插入状态来更新状态(旁注:splice(0,0,x) 是 unshift(x)),然后渲染:

<div>
 <div key={0}><input defaultValue="foo" /></div>
 <div key={1}><input defaultValue="1" /></div>
 <div key={2}><input defaultValue="2" /></div>
 <div key={3}><input defaultValue="3" /></div>
</div>
<div>
 <div key={0}><span>foo</span></div>
 <div key={1}><span>1</span></div>
 <div key={2}><span>2</span></div>
 <div key={3}><span>3</span></div>
</div>

现在是时候让 React 接受这两个,并找出发生了什么变化。为此,它会比较组件(此处为 div、span 或输入)和键。

从 span 部分开始,它看到所有标签都是相同的,但在末尾有一个它以前从未见过的新键它也看到了该值div[0] spandiv[1] span以及div[2] span都发生了变化。在末尾插入带有文本 3元素,并更新其他跨度。效率低下,但它有效。

现在对于输入......它做了大致相同的事情。前三个输入的键和标签是相同的,它告诉每个输入它们正在更新,它们检查它们是否有值props,如果没有,请拒绝做任何事情。

它看到最后有一个新的输入。它位于div[3] input并且默认值为“3”。React 插入新输入,将值设置为 3,更新完成。

随着您继续,上述过程是相同的,它会继续更新跨度,div span并且div input每次都插入一个新的

除了我认为不应该使用的 defaultValue 之外,这里的主要问题是键是颠倒的!项目在开头插入,因此键应该下降,而不是上升。这可以通过使用项目的长度并从中减去索引来解决。如果长度为 4,则密钥为 4、3、2、1。

this.state.app.data.map(function(page, i, items){
  return(
    <div key={items.length - i}>{self.renderElement(page)}</div>
  );
})

然后 React 说“哦,开头有一个新项目,我应该在那里插入一个节点”,然后每个人都很高兴。结束。

抱歉,我不知道发生这种情况的确切原因,但如果您在输入中包含关键属性的值,则可以解决问题:

  renderElement: function(i){
    return(
      <input key={i} defaultValue={i} />
    );
  },

我的猜测是缺少 'key' 和使用 'defaultValue' 而不是 'value',使得 React 很难正确区分更改。

非常感谢您解决问题。但我想知道为什么只有输入元素有这个问题。
2021-06-17 06:59:29
@ColinRamsay 这是一个具有键的 div 数组,每个键都有一个非数组子元素。警告不包括这一点。此解决方案确实有效,但只能通过使其重新创建大部分或所有输入(值为 b、c、c、1、2、3,在添加值后,第一个 c 将位于第二个 c 的先前位置,并且键会匹配)。
2021-06-18 06:59:29