Reactjs - 在动态元素渲染中将 ref 添加到输入
Reactjs - Adding ref to input in dynamic element render
我正在尝试 focus/highlight 在 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
元素上工作。
您正在循环中设置 ref
,如您所知,ref
通过 this
关键字设置为 class
。这意味着您正在设置多个 refs
但覆盖 class
.
中的同一个
一种解决方案(不是理想的解决方案)是以不同的方式命名它们,也许将密钥添加到每个 ref
名称:
ref={input => {
this[`textInput${i}`] = input;
}}
并且当您针对 Button
的 onClick
事件时,您应该使用相同的键作为参数:
action={
<Button
inverted
color="blue"
icon="copy"
onClick={this.handleClick(i)}
>
Focus
</Button>
}
现在,点击事件应该改变并接受 id
作为参数并触发相关的 ref
(我在这里使用柯里化):
handleClick = (id) => (e) => {
this[`textInput${id}`].focus();
}
请注意,这是更简单的解决方案,但不是理想的解决方案,因为我们在每个渲染器上创建了一个函数的新实例,因此我们传递了一个新的道具,它可以中断反应的差异算法(更好更多"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"));
更好更"react'ish"的解决方案是使用组件组合或包装Button
的HOC并注入一些简单的逻辑,比如pass 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"));
编辑
作为您编辑的跟进:
but when there are multiple Card elements, it still only works for the
last one.
发生这种情况的原因与之前相同,您对两个数组使用相同的 i
。
这是一个简单的解决方案,同时使用 index
和 i
作为您的 ref
名称。
设置 ref
名称:
ref={input => { this[`textInput${index}${i}`] = input }}
将名称传递给处理程序:
<Button onClick={this.handleClick(`${index}${i}`)}></Button>
我修改了我的问题并提供了第二个被认为是最佳实践的解决方案。再次阅读我的回答,看看不同的方法。
完成运行示例:
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;
我正在尝试 focus/highlight 在 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
元素上工作。
您正在循环中设置 ref
,如您所知,ref
通过 this
关键字设置为 class
。这意味着您正在设置多个 refs
但覆盖 class
.
中的同一个
一种解决方案(不是理想的解决方案)是以不同的方式命名它们,也许将密钥添加到每个 ref
名称:
ref={input => {
this[`textInput${i}`] = input;
}}
并且当您针对 Button
的 onClick
事件时,您应该使用相同的键作为参数:
action={
<Button
inverted
color="blue"
icon="copy"
onClick={this.handleClick(i)}
>
Focus
</Button>
}
现在,点击事件应该改变并接受 id
作为参数并触发相关的 ref
(我在这里使用柯里化):
handleClick = (id) => (e) => {
this[`textInput${id}`].focus();
}
请注意,这是更简单的解决方案,但不是理想的解决方案,因为我们在每个渲染器上创建了一个函数的新实例,因此我们传递了一个新的道具,它可以中断反应的差异算法(更好更多"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"));
更好更"react'ish"的解决方案是使用组件组合或包装Button
的HOC并注入一些简单的逻辑,比如pass 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"));
编辑
作为您编辑的跟进:
but when there are multiple Card elements, it still only works for the last one.
发生这种情况的原因与之前相同,您对两个数组使用相同的 i
。
这是一个简单的解决方案,同时使用 index
和 i
作为您的 ref
名称。
设置 ref
名称:
ref={input => { this[`textInput${index}${i}`] = input }}
将名称传递给处理程序:
<Button onClick={this.handleClick(`${index}${i}`)}></Button>
我修改了我的问题并提供了第二个被认为是最佳实践的解决方案。再次阅读我的回答,看看不同的方法。
完成运行示例:
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;