Below is my parent component with multiple inputs from a loop. How can I choose one input
to focus? Do I have to create a dynamic ref
in this case?
// General Focus Hook
const useFocus = (initialFocus = false, id = "") => {
const [focus, setFocus] = useState(initialFocus)
return ([
(newVal=true) => setFocus(newVal), {
autoFocus: focus,
key: `${id}${focus}`,
onFocus: () => setFocus(true),
onBlur: () => setFocus(false),
},
])
}
const data: [{
name: "abc"
},{
name: "def"
}]
const TestRef = () => {
const focusHelper = data.map( (_,i) => {
const [setFocus, focusProps]= useFocus(false, i)
return {setFocus, focusProps}
})
return (
<div>
{data.map( (o,i) => (
<Hello placeholder={o.name} {...focusHelper[i].focusProps} />;
))}
<button onClick={() => focusHelper[0].setFocus()}>focus input 1</button>
<button onClick={() => focusHelper[1].setFocus()}>focus input 2</button>
</div>
);
}
You can find more info here: Set focus on input after render
You can use callback refs to generate and store the dynamic ref of each input in an array. Now you can refer to them using the index of the ref:
const Hello = React.forwardRef((props, ref) => <input ref={ref} />);
class Button extends React.Component {
onClick = () => this.props.onClick(this.props.id);
render() {
return (
<button onClick={this.onClick}>{this.props.children}</button>
);
}
}
class TestRef extends React.Component {
state = {
data: [
{
name: "abc"
},
{ name: "def" }
]
};
inputRefs = [];
setRef = (ref) => {
this.inputRefs.push(ref);
};
focusInput = (id) => this.inputRefs[id].focus();
render() {
return (
<div>
{this.state.data.map(({ name }) => (
<Hello
placeholder={name}
ref={this.setRef}
key={name} />
))}
<Button onClick={this.focusInput} id={0}>focus input 1</Button>
<Button onClick={this.focusInput} id={1}>focus input 2</Button>
</div>
);
}
}
ReactDOM.render(<TestRef />, document.getElementById("root"));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
I discovered another way of tackling this:
let dataCount = 0;
class TestRef extends React.Component {
state = {
data: [
{
name: "abc"
},
{ name: "def" }
]
};
focusInput = (thisHello) => this[`ref${thisHello}`].current.focus();
render() {
return (
<div>
{this.state.data.map(o => {
dataCount++
return <Hello placeholder={o.name} ref={(el) => { this[`ref${dataCount}`] = el; }} />;
})}
<button onClick={() => this.focusInput(1)}>focus input 1</button>
<button onClick={() => this.focusInput(2)}>focus input 2</button>
</div>
);
}
}
The dataCount
is unnecessary if your Hello element has a key or unique ID to use as a variable.
if you are coming to this question 2020 here is how you create multiple refs using create hooks in a loop
const MyComponent=(){
// empty list to put our refs in
let LiRefs = []
return (
<React.Fragment>
<ul className="event-list">
// this part i am checking if my data exist first other wise load spinner
{newData ? (
newData.map((D) => {
// the cool part inside the loop
//create new ref
//push it into arr
const newRef = createRef();
LiRefs.push(newRef);
return (
// link it to ur li
// voila you have list of refs that points to your list items
<li key={D._id} ref={newRef}>
title : {D.title} <br />
description : {D.description}
<br />
data : {D.date} <br />
price : {D.price}
<br />
<div>creator : {D.creator.username}</div>
{authData.token && (
<button type="button" id={D._id} onClick={handelBooking}>
Book
</button>
)}
</li>
);
})
) : (
<Spinner />
)}
</ul>
</React.Fragment>
);
}