Reactjs - 在动态元素渲染中添加 ref 到输入
Posted
技术标签:
【中文标题】Reactjs - 在动态元素渲染中添加 ref 到输入【英文标题】:Reactjs - Adding ref to input in dynamic element render 【发布时间】: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
元素上工作。
【问题讨论】:
React Select mapping issue的可能重复 不同之处在于,除了 handleClick 之外,输入上的其他方法对每个元素都可以正常工作。 ref 只选择最后渲染的元素,不选择其他元素。 @MerrilJeffs 第二个代码将按预期工作。您在控制台上收到第二个代码的任何错误吗? 显示您的Input
组件的代码以了解您如何实现操作 (Button
) 事件
@MerrilJeffs 第二个代码正在运行。这是工作示例codesandbox.io/s/p3y90wmp7m
【参考方案1】:
您正在循环内设置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
(我在这里使用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
。
这是一个简单的解决方案,请同时使用 index
和 i
作为您的 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;
【讨论】:
以上是关于Reactjs - 在动态元素渲染中添加 ref 到输入的主要内容,如果未能解决你的问题,请参考以下文章