【问题标题】:Reactjs - Adding ref to input in dynamic element renderReactjs - 在动态元素渲染中添加 ref 到输入
【发布时间】:2018-03-10 12:17:41
【问题描述】:

我正在尝试在 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 元素上工作。

【问题讨论】:

  • 不同之处在于,除了 handleClick 之外,输入上的其他方法对每个元素都可以正常工作。 ref 只选择最后渲染的元素,不选择其他元素。
  • @MerrilJeffs 第二个代码将按预期工作。您在控制台上收到第二个代码的任何错误吗?
  • 显示您的Input 组件的代码以了解您如何实现操作 (Button) 事件
  • @MerrilJeffs 第二个代码正在运行。这是工作示例codesandbox.io/s/p3y90wmp7m

标签: javascript reactjs jsx semantic-ui-react


【解决方案1】:

您正在循环内设置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(我在这里使用currying):

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

请注意,这是一个更简单的解决方案,但不是理想的解决方案,因为我们在每个渲染上创建一个函数的新实例,因此我们传递一个新的 prop 可以中断 react 的差异算法(更好和更多 “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"));

编辑
作为您编辑的后续行动:

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

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

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

将名称传递给处理程序:

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

Working example

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

【讨论】:

  • 感谢您的意见!您的示例效果很好,但现在我正在尝试使用多个父组件。我在原始问题中发布了一个示例。
  • 谢谢.. 这是完美的!
  • 这正是我的解决方案。谢谢!
【解决方案2】:

完整的运行示例:

import React,{useRef} from 'react';

function Userlist(){
    let toogleRef = useRef([])
        
    const userlist = [
        {name:'ankit',email:'a@gmail.com',phone:222},
        {name:'verma',email:'v@gmail.com',phone:33},
        {name:'sumit',email:'s@gmail.com',phone:444},
    ];
    function toogleFun(ind){
        
        console.log(toogleRef.current);
        toogleRef.current[ind].style.display="none";        
    }
    return(
    <>
        <table>
        <tbody>
            <tr>
                <th>Id</th>
                <th>Name</th>
                <th>Email</th>
                <th>Phone</th>
                <th>Toggle</th>
            </tr>
            {
                userlist.map((item, index)=>
                        <tr key={index}  ref={(element) => toogleRef.current.push(element)}>
                            <td>{index + 1}</td>
                            <td>{item.name}</td>
                            <td>{item.email}</td>
                            <td>{item.phone}</td>
                            <td><button onClick={()=>toogleFun(index)}>Toogle</button></td>
                        </tr>
                )   
            }
        </tbody>
        </table>
    </>
    )
}
export default Userlist;

【讨论】:

    猜你喜欢
    • 2019-01-02
    • 2020-10-09
    • 2021-08-02
    • 2020-02-20
    • 2021-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多