Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deal with a ref within a loop?

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>     );   } } 
like image 621
Alisa T Morgan Avatar asked Sep 21 '18 16:09

Alisa T Morgan


People also ask

How do you stop an infinite loop reaction?

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.

What is the difference between useRef and createRef?

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.

What is REF IN React hooks?

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 .


1 Answers

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>
like image 194
Ori Drori Avatar answered Sep 22 '22 18:09

Ori Drori