代码之家  ›  专栏  ›  技术社区  ›  merrilj

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

  •  14
  • merrilj  · 技术社区  · 7 年前

    我试图在单击React时聚焦/突出显示输入文本。它按预期工作,但仅适用于渲染数组中的最后一个元素。我试过几种不同的方法,但都是一样的。以下是我拥有的两个例子:

    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 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 卡片 元素,它仍然只适用于最后一个元素。二者都 Buttons 分别为其输入工作,但仅限于最后一个 卡片 元素渲染。

    2 回复  |  直到 7 年前
        1
  •  12
  •   Sagiv b.g    7 年前

    ref 正如你们已经知道的,在一个循环中 设置为 class 通过 this 关键词。这意味着您正在设置多个 refs
    一种解决方案(不是理想的解决方案)是对它们进行不同的命名,可能会为每一个添加键

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

    当你瞄准它的时候 onClick 的事件 Button

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

    现在,click事件应该更改并接受 id 作为参数并触发相关 裁判

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

    请注意,这是一个简单的解决方案,但不是理想的解决方案,因为我们在每个渲染上创建了一个新的函数实例,因此我们传递了一个新的道具,该道具可以中断react的扩散算法(更好、更 “反应很快” 下一步)。

    赞成的意见:

    • 更快地实施

    欺骗:

    • 可能导致性能问题

    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"));
    

    更好、更多 解决方案是使用组件组合或包装 并注入一些简单的逻辑,比如传递 而不是在父级中使用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 对于两个阵列。
    index 为了您的 姓名。
    设置 姓名:

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

    将名称传递给处理程序:

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

    Working example

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

        2
  •  1
  •   Ghost Ops Tahir77667    3 年前

    完整的运行示例:

    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;