2017-09-28 107 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輕鬆修復,閱讀我的編輯 –