Reactjs - 在动态元素渲染中添加ref到输入

mer*_*ilj 9 javascript jsx reactjs 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>
    )
}
Run Code Online (Sandbox Code Playgroud)

如果只有一个元素被渲染,它会将文本聚焦在输入中,但如果有多个元素,则每个元素的按钮单击仅选择最后一个元素的输入.这是另一个例子:

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>
    )
}
Run Code Online (Sandbox Code Playgroud)

这两种方法基本上都以相同的方式响应.我需要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>
    )
}
Run Code Online (Sandbox Code Playgroud)

现在,在修改后的代码中,您的方法适用于一个Card元素,但是当有多个Card元素时,它仍然只适用于最后一个元素.两者Input Buttons分别用于输入,但仅用于Card渲染的最后一个元素.

Sag*_*b.g 8

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

        ref={input => {
          this[`textInput${i}`] = input;
        }}
Run Code Online (Sandbox Code Playgroud)

并且当您针对的onClick事件定位时,Button应使用相同的键作为参数:

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

现在,click事件应该更改并接受id作为参数并触发相关事件ref(我在这里使用currying):

  handleClick = (id) => (e) => {
      this[`textInput${id}`].focus();
  }
Run Code Online (Sandbox Code Playgroud)

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

优点:

  • 易于实施
  • 实施更快

缺点:

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

工作实例

这是完整的代码:

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"));
Run Code Online (Sandbox Code Playgroud)

更好,更“反应”的解决方案是使用组件组合或包装的HOC Button并注入一些简单的逻辑,如传递id父级而不是使用2个函数。

优点:

  • 如前所述,出现性能问题的机会更少
  • 您可以重用此组件和逻辑
  • 有时更容易调试

缺点:

  • 更多代码编写

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

一个工作示例
完整代码:

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"));
Run Code Online (Sandbox Code Playgroud)

编辑
作为编辑的后续步骤:

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

发生这种情况的原因与以前相同i,两个数组都使用相同的原因。
这是一个简单的解决方案,请同时使用indexi作为您的ref名字。
设置ref名称:

ref={input => { this[`textInput${index}${i}`] = input }}
Run Code Online (Sandbox Code Playgroud)

将名称传递给处理程序:

<Button onClick={this.handleClick(`${index}${i}`)}></Button>
Run Code Online (Sandbox Code Playgroud)

工作实例

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