Reactjs - 在动态元素渲染中添加引用输入

时间:2017-09-28 16:52:43

标签: javascript reactjs jsx semantic-ui-react

我试图在React中关注/突出显示输入文本onClick。它按预期工作,但仅适用于渲染数组中的最后一个元素。我尝试了几种不同的方法,但它们都做了完全相同的事情。以下是我的两个例子:

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组件是从Semantic UI React导入的,我的应用中没有其他实现

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

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元素时,它仍然只适用于最后一个元素。 Input Buttons分别适用于他们的输入,但仅适用于最后呈现的Card元素。

1 个答案:

答案 0 :(得分:6)

您正在循环中设置ref,如您所知,ref通过class关键字设置为this。这意味着您要设置多个refs,但会覆盖class内的同一个{。}} 一种解决方案(不是理想的解决方案)是以不同的方式命名,也可以为每个ref名称添加密钥:

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

当您定位onClick的{​​{1}}事件时,您应该使用相同的密钥作为参数:

Button

现在,点击事件应该更改并接受 action={ <Button inverted color="blue" icon="copy" onClick={this.handleClick(i)} > Focus </Button> } 作为参数并触发相关的id(我在这里使用currying):

ref

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

优点:

  • 更容易实施
  • 更快实施

<强>缺点:

  • 可能会导致性能问题
  • 减少反应成分的方式

Working example

这是完整的代码:

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

更好,更多&#34;反应&#34; 解决方案是使用组件组合或包裹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")); 并注入一些简单逻辑的HOC,比如传递Button而不是在父级中使用2个函数。

优点:

  • 如上所述,性能问题的可能性较小
  • 您可以重复使用此组件和逻辑
  • 有时更容易调试

缺点:

  • 更多代码编写

  • 维护/测试等的另一个组件。

A working example
完整的代码:

id

修改
作为编辑的后续内容:

  

但是当有多个Card元素时,它仍然只适用于   最后一个。

出现这种情况的原因与之前相同,您对两个阵列使用相同的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 }}

Working example

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