J'essaie de concentrer/mettre en surbrillance le texte d'entrée surCliquez dans React. Il fonctionne comme prévu, mais uniquement sur le dernier élément du tableau rendu. J'ai essayé plusieurs méthodes différentes, mais elles font toutes exactement la même chose. Voici deux exemples de ce que j'ai:
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>
)
}
S'il n'y a qu'un seul élément en cours de rendu, il concentre le texte dans l'entrée, mais s'il y a plusieurs éléments, chaque clic sur le bouton de l'élément ne sélectionne que l'entrée du dernier élément. Voici un autre exemple:
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>
)
}
Ces deux méthodes répondent essentiellement de la même manière. J'ai besoin du focus d'entrée handleClick pour travailler pour chaque élément rendu dynamiquement. Tout conseil est grandement appréciée. Merci d'avance!
Le composant Input
est importé de l'interface utilisateur sémantique React sans implémentations supplémentaires dans mon application
MISE À JOUR Merci les gars pour les bonnes réponses. Les deux méthodes fonctionnent très bien dans un rendu d'élément de boucle unique, mais maintenant j'essaie de l'implémenter avec plusieurs éléments parents. Par exemple:
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>
)
}
Maintenant, dans le code modifié, vos méthodes fonctionnent très bien pour un élément Card
, mais lorsqu'il y a plusieurs éléments Card
, cela ne fonctionne que pour le dernier. Les deux Input
Buttons
fonctionnent respectivement pour leurs entrées, mais uniquement sur le dernier élément Card
rendu.
Vous définissez un ref
dans une boucle, comme vous le savez déjà, le ref
est défini sur le class
via le mot clé this
. Cela signifie que vous définissez plusieurs refs
mais que vous remplacez le même à l'intérieur du class
.
Une solution (pas la solution idéale) consiste à les nommer différemment, peut-être ajouter la clé à chaque nom ref
:
ref={input => {
this[`textInput${i}`] = input;
}}
et lorsque vous ciblez cet événement onClick
de Button
, vous devez utiliser la même clé comme paramètre:
action={
<Button
inverted
color="blue"
icon="copy"
onClick={this.handleClick(i)}
>
Focus
</Button>
}
Maintenant, l'événement click doit changer et accepter le id
comme paramètre et déclencher le ref
approprié (j'utilise le currying ici):
handleClick = (id) => (e) => {
this[`textInput${id}`].focus();
}
Notez que c'est une solution plus facile mais pas la solution idéale, car nous créons une nouvelle instance d'une fonction sur chaque rendu, donc nous passons un nouvel accessoire qui peut interrompre l'algorithme différent de react (un meilleur et plus " react'ish " chemin à venir ensuite).
Avantages:
Inconvénients:
Voici le code complet:
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"));
Une solution meilleure et plus "react'ish" consisterait à utiliser la composition des composants ou un HOC qui enveloppe le Button
et à injecter une logique simple, comme passer le id
au lieu d'utiliser 2 fonctions dans le parent.
Avantages:
Inconvénients:
Plus d'écriture de code
Un autre composant pour maintenir/tester etc ..
n exemple de travail
Le code complet:
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"));
Modifier
Pour faire suite à votre modification:
mais lorsqu'il y a plusieurs éléments Card, cela ne fonctionne que pour le dernier.
Cela se produit pour la même raison que précédemment, vous utilisez le même i
pour les deux tableaux.
Ceci est une solution simple, utilisez à la fois index
et i
pour vos noms ref
.
Définition du nom ref
:
ref={input => { this[`textInput${index}${i}`] = input }}
Passer le nom au gestionnaire:
<Button onClick={this.handleClick(`${index}${i}`)}></Button>
J'ai modifié ma question et fourni une deuxième solution considérée comme la meilleure pratique. relisez ma réponse et voyez les différentes approches.