2017-09-28 76 views
2

我正试图在onClick中重点/突出显示输入文字onClick。它按预期工作,但只在渲染数组的最后一个元素上。我尝试了几种不同的方法,但他们都做了完全相同的事情。这里是什么,我有两个例子:Reactjs - 在动态元素渲染中添加ref输入

export default class Services extends Component { 

handleFocus(event) { 
    event.target.select() 
} 

handleClick() { 
    this.textInput.focus() 
} 


render() { 
    return (
     <div> 
      {element.sources.map((el, i) => (
       <List.Item key={i}> 
       <Segment style={{marginTop: '0.5em', marginBottom: '0.5em'}}> 
        <Input fluid type='text' 
         onFocus={this.handleFocus} 
         ref={(input) => { this.textInput = input }} 
         value='text to copy' 
         action={ 
          <Button inverted color='blue' icon='copy' onClick={() => this.handleClick}></Button> 
         } 
        /> 
       </Segment> 
       </List.Item> 
      ))} 
     </div> 
    ) 
} 

如果只有一个被渲染的元素,它的重点在输入文本,但是如果有多个元素,每个元素的按一下按钮只选择最后一个元素的输入。这里有另外一个例子:

export default class Services extends Component { 

constructor(props) { 
    super(props) 

    this._nodes = new Map() 
    this._handleClick = this.handleClick.bind(this) 
} 

handleFocus(event) { 
    event.target.select() 
} 

handleClick(e, i) { 
    const node = this._nodes.get(i) 
    node.focus() 
} 


render() { 
    return (
     <div> 
      {element.sources.map((el, i) => (
       <List.Item key={i}> 
       <Segment style={{marginTop: '0.5em', marginBottom: '0.5em'}}> 
        <Input fluid type='text' 
         onFocus={this.handleFocus} 
         ref={c => this._nodes.set(i, c)} 
         value='text to copy' 
         action={ 
          <Button inverted color='blue' icon='copy' onClick={e => this.handleClick(e, i)}></Button> 
         } 
        /> 
       </Segment> 
       </List.Item> 
      ))} 
     </div> 
    ) 
} 

这两种方法基本上都有相同的响应方式。我需要handleClick输入焦点为每个动态呈现的元素工作。任何意见是极大的赞赏。提前致谢!

Input组件从语义UI进口没有额外的实现作出反应,我的应用程序

UPDATE 谢谢你们为伟大的答案。这两种方法在单个循环元素渲染中都很有效,但现在我试图用多个父元素来实现它。例如:

import React, { Component } from 'react' 
import { Button, List, Card, Input, Segment } from 'semantic-ui-react' 

export default class ServiceCard extends Component { 

handleFocus(event) { 
    event.target.select() 
} 

handleClick = (id) => (e) => { 
    this[`textInput${id}`].focus() 
} 

render() { 
    return (
     <List divided verticalAlign='middle'> 
      {this.props.services.map((element, index) => (
       <Card fluid key={index}> 
        <Card.Content> 
         <div> 
          {element.sources.map((el, i) => (
           <List.Item key={i}> 
            <Segment> 
             <Input fluid type='text' 
              onFocus={this.handleFocus} 
              ref={input => { this[`textInput${i}`] = input }} 
              value='text to copy' 
              action={ 
               <Button onClick={this.handleClick(i)}></Button> 
              } 
             /> 
            </Segment> 
           </List.Item> 
          ))} 
         </div> 
        </Card.Content> 
       </Card> 
      ))} 
     </List> 
    ) 
} 

现在,在修改后的代码,你的方法工作了,一个Card元素,但是当有多个Card元素,它仍然只适用于最后一个。 InputButtons分别适用于它们的输入,但仅在最后的Card元素呈现。

+0

[React Select映射问题]的可能重复(https://stackoverflow.com/questions/46467577/react-select-mapping-issue) – bennygenel

+0

它的不同之处在于,输入上的其他方法对每个元素都能正常工作除了handleClick。 ref只选择最后一个呈现的元素,没有其他元素。 – merrilj

+0

@MerrilJeffs第二个代码将按预期工作。第二个代码在控制台上是否出现错误? –

回答

0

您正在设置一个循环内的ref,如您所知,ref通过this关键字设置为class。这意味着您正在设置多个refs,但覆盖class中的同一个。
一个解决方案(而不是理想的解决方案)是不同的命名它们,也许键添加到每个ref名称:

 ref={input => { 
      this[`textInput${i}`] = input; 
     }} 

,当你的目标是,ButtononClick情况下,您应该使用相同的密钥作为参数:

action={ 
        <Button 
        inverted 
        color="blue" 
        icon="copy" 
        onClick={this.handleClick(i)} 
        > 
        Focus 
        </Button> 
       } 

现在,点击事件应该改变并接受id作为参数,并触发相关ref(我使用钻营这里):

handleClick = (id) => (e) => { 
     this[`textInput${id}`].focus(); 
    } 

请注意,这是更简单的解决方案,但不是理想的解决方案,因为我们在每个渲染上创建了一个函数的新实例,因此我们传递了一个新的支持,可以中断反应的差异化算法(更好,更多“react'ish”接下来的方式)。

优点:

  • 更容易实现
  • 更快地实现

缺点:

  • 可能会导致性能问题
  • 欠反应的组分的方法

Working example

这是完整代码:

class Services extends React.Component { 

    handleFocus(event) { 
    event.target.select(); 
    } 


    handleClick = id => e => { 
    this[`textInput${id}`].focus(); 
    }; 

    render() { 
    return (
     <div> 
     {sources.map((el, i) => (
      <List.Item key={i}> 
      <Segment style={{ marginTop: "0.5em", marginBottom: "0.5em" }}> 
       <Input 
       fluid 
       type="text" 
       onFocus={this.handleFocus} 
       ref={input => { 
        this[`textInput${i}`] = input; 
       }} 
       value="text to copy" 
       action={ 
        <Button 
        inverted 
        color="blue" 
        icon="copy" 
        onClick={this.handleClick(i)} 
        > 
        Focus 
        </Button> 
       } 
       /> 
      </Segment> 
      </List.Item> 
     ))} 
     </div> 
    ); 
    } 
} 

render(<Services />, document.getElementById("root")); 

更好和更“react'ish”的解决办法是使用组分的组合物或一个包装Button并注入一些简单逻辑的HOC,比如通过id而不是在父级中使用2个函数。

优点:

  • 如前所述,性能问题
  • 可以重新使用该组件和逻辑
  • 有时更容易调试

缺点机会少:

  • 更多

    代码编写

  • 另一种组分来维持/测试等。

A working example
的完整代码:

class MyButton extends React.Component { 

    handleClick = (e) => { 
    this.props.onClick(this.props.id) 
    } 

    render() { 
    return (
     <Button 
     {...this.props} 
     onClick={this.handleClick} 
     > 
     {this.props.children} 
     </Button> 
    ) 
    } 
} 


class Services extends React.Component { 

    constructor(props){ 
    super(props); 
    this.handleClick = this.handleClick.bind(this); 
    } 

    handleFocus(event) { 
    event.target.select(); 
    } 


    handleClick(id){ 
    this[`textInput${id}`].focus(); 
    }; 

    render() { 
    return (
     <div> 
     {sources.map((el, i) => (
      <List.Item key={i}> 
      <Segment style={{ marginTop: "0.5em", marginBottom: "0.5em" }}> 
       <Input 
       fluid 
       type="text" 
       onFocus={this.handleFocus} 
       ref={input => { 
        this[`textInput${i}`] = input; 
       }} 
       value="text to copy" 
       action={ 
        <MyButton 
        inverted 
        color="blue" 
        icon="copy" 
        onClick={this.handleClick} 
        id={i} 
        > 
        Focus 
        </MyButton> 
       } 
       /> 
      </Segment> 
      </List.Item> 
     ))} 
     </div> 
    ); 
    } 
} 

render(<Services />, document.getElementById("root")); 

编辑
作为后续到您的编辑:

但是当存在多个卡片元素时,它仍然只能用于上一个 。

发生这种情况的原因与之前相同,因此对两个阵列使用相同的i
这是一个简单的解决方案,请使用indexi作为您的ref名称。
设置ref名称:

ref={input => { this[`textInput${index}${i}`] = input }} 

薪火名到处理程序:

<Button onClick={this.handleClick(`${index}${i}`)}></Button> 

Working example

我修改我的问题,并提供了被认为是最佳的第二溶液。再次阅读我的答案,并看到不同的方法。

+0

谢谢您的输入!你的例子很好,但现在我用多个父组件来尝试它。我在原始问题中发布了一个例子。 – merrilj

+0

@MerrilJeffs轻松修复,阅读我的编辑 –