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?
class TestRef extends React.Component { ref = React.createRef(); state = { data: [ { name: "abc" }, { name: "def" } ] }; focusInput = () => this.ref.current.focus(); render() { return ( <div> {this.state.data.map(o => { return <Hello placeholder={o.name} ref={this.ref} />; })} <button onClick={this.focusInput}>focus input 1</button> <button onClick={this.focusInput}>focus input 2</button> </div> ); } }
To get rid of your infinite loop, simply use an empty dependency array like so: const [count, setCount] = useState(0); //only update the value of 'count' when component is first mounted useEffect(() => { setCount((count) => count + 1); }, []); This will tell React to run useEffect on the first render.
useRef: The useRef is a hook that uses the same ref throughout. It saves its value between re-renders in a functional component and doesn't create a new instance of the ref for every re-render. It persists the existing ref between re-renders. createRef: The createRef is a function that creates a new ref every time.
Mutable values. useRef(initialValue) is a built-in React hook that accepts one argument as the initial value and returns a reference (aka ref). A reference is an object having a special property current .
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>
If the list wouldn't be static, and items may be removed/replaced, you should probably use a WeakMap to hold the refs, or any other method of adding the ref by a constant id
. You should also check before using the ref, because it might not exist:
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" }, { name: "ghi" }] }; componentDidMount() { setTimeout(() => { this.setState(({ data }) => ({ data: data.slice(0, -1) })) }, 3000); } inputRefs = new WeakMap; setRef = (id) => (ref) => { this.inputRefs.set(id, ref); }; focusInput = (id) => { const input = this.inputRefs.get(id); if(input) input.focus(); // use only if the ref exists - use optional chaining ?. if possible instead } render() { const { data } = this.state; return ( <div> {data.map(o => ( <Hello placeholder={o.name} ref={this.setRef(o)} key={o.name} /> ))} <br /> {data.map((o, i) => ( <Button onClick={this.focusInput} id={o} key={o.name}>focus input {i + 1}</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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With