개발/React

리액트 - useRef로 특정 위치로 포커스 이동하기 (Use Ref Hook Focus)

피로물든딸기 2023. 6. 17. 15:06
반응형

리액트 전체 링크

 

focus test 버튼을 누르면 input으로 이동해서 포커싱이 되도록 해보자.

아래의 코드로 시작한다.

import { useState, useEffect } from "react";

function App() {
  const [temp, setTemp] = useState([]);

  useEffect(() => {
    let t = [];
    for (let i = 0; i < 100; i++) t.push(i);
    setTemp(t);
  }, []);

  return (
    <div className="App">
      <button>focus test</button>

      {temp.map((item, idx) => (
        <p key={idx}>{item}번째 p tag</p>
      ))}
      <input type="text"/>
    </div>
  );
}

export default App;

 

focus test 버튼을 누르면 맨 아래의 input으로 이동하도록 해보자.


useRef 선언

 

focus 기능을 사용하기 위해서는 useRef를 이용한다.

import { useState, useEffect, useRef } from "react";

function App() {
  const focusTest = useRef(null);

 

만든 useRef인  focusTest를 inputref에 설정하자.

<input type="text" ref={focusTest}></input>

 

이제 버튼을 누르면 아래의 input으로 이동한다.

 

전체 코드는 다음과 같다.

import { useState, useEffect, useRef } from "react";

function App() {
  const focusTest = useRef(null);
  
  const [temp, setTemp] = useState([]);

  useEffect(() => {
    let t = [];
    for (let i = 0; i < 100; i++) t.push(i);
    setTemp(t);
  }, []);


  return (
    <div className="App">
      <button onClick={() => focusTest.current.focus()}>focus test</button>

      {temp.map((item, idx) => (
        <p key={idx}>{item}번째 p tag</p>
      ))}
      <input type="text"></input>
    </div>
  );
}

export default App;

여러 개의 useRef

 

여러 input으로 이동하려면 useRef를 더 선언하면 된다.

예를 들면 아래와 같다.

import { useState, useEffect, useRef } from "react";

function App() {
  const focusTest = useRef(null);
  const focusTest2 = useRef(null);
  const focusTest3 = useRef(null);
  
  const [temp, setTemp] = useState([]);

  useEffect(() => {
    let t = [];
    for (let i = 0; i < 100; i++) t.push(i);
    setTemp(t);
  }, []);


  return (
    <div className="App">
      <button onClick={() => focusTest.current.focus()}>focus test</button>
      <button onClick={() => focusTest2.current.focus()}>focus test2</button>
      <button onClick={() => focusTest3.current.focus()}>focus test3</button>

      {temp.map((item, idx) => (
        <p key={idx}>{item}번째 p tag</p>
      ))}
      <input type="text" ref={focusTest}></input>
      <input type="text" ref={focusTest2}></input>
      <input type="text" ref={focusTest3}></input>
      

    </div>
  );
}

export default App;

 

위의 코드라면 focus test3 버튼을 누르면 번째 input으로 이동한다.

 

하지만 더 간결하게 하려면 useRef배열로 선언하면 된다.

const focusTest = useRef([]);

 

inputref는 아래와 같이 코드가 변경된다.

      <input type="text" ref={(val) => (focusTest.current[0] = val)}></input>
      <input type="text" ref={(val) => (focusTest.current[1] = val)}></input>
      <input type="text" ref={(val) => (focusTest.current[2] = val)}></input>

 

focus()를 호출하는 부분은 아래와 같이 변하게 된다.

      <button onClick={() => focusTest.current[0].focus()}>focus test</button>
      <button onClick={() => focusTest.current[1].focus()}>focus test2</button>
      <button onClick={() => focusTest.current[2].focus()}>focus test3</button>

 

전체 코드는 다음과 같다.

import { useState, useEffect, useRef } from "react";

function App() {
  const focusTest = useRef([]);

  const [temp, setTemp] = useState([]);

  useEffect(() => {
    let t = [];
    for (let i = 0; i < 100; i++) t.push(i);
    setTemp(t);
  }, []);

  return (
    <div className="App">
      <button onClick={() => focusTest.current[0].focus()}>focus test</button>
      <button onClick={() => focusTest.current[1].focus()}>focus test2</button>
      <button onClick={() => focusTest.current[2].focus()}>focus test3</button>

      {temp.map((item, idx) => (
        <p key={idx}>{item}번째 p tag</p>
      ))}
      <input type="text" ref={(val) => (focusTest.current[0] = val)}></input>
      <input type="text" ref={(val) => (focusTest.current[1] = val)}></input>
      <input type="text" ref={(val) => (focusTest.current[2] = val)}></input>
    </div>
  );
}

export default App;


<p> 태그에 포커스 맞추기 (tabIndex)

 

먼저 아래와 같이 코드를 수정해서 N번째 p 태그로 이동하도록 해보자.

input에 입력받은 태그로 이동하도록 useStateinput + button을 추가하였다.

import { useState, useEffect, useRef } from "react";

function App() {
  const focusTest = useRef([]);
  const pTagFocus = useRef([]); /* <p> 태그 포커스 용 useRef */

  const [temp, setTemp] = useState([]);
  const [number, setNumber] = useState("");

  useEffect(() => {
    let t = [];
    for (let i = 0; i < 100; i++) t.push(i);
    setTemp(t);
  }, []);

  return (
    <div className="App">
      <input value={number} onChange={(e) => setNumber(e.target.value)} />
      <button
        onClick={() => {
          pTagFocus.current[number].focus();
        }}
      >
        번째 p 태그로 이동
      </button>
      <button onClick={() => focusTest.current[0].focus()}>focus test</button>
      <button onClick={() => focusTest.current[1].focus()}>focus test2</button>
      <button onClick={() => focusTest.current[2].focus()}>focus test3</button>

      {temp.map((item, idx) => (
        <p
          key={idx}
          ref={(val) => (pTagFocus.current[idx] = val)}
        >
          {item}번째 p tag
        </p>
      ))}
      <input type="text" ref={(val) => (focusTest.current[0] = val)}></input>
      <input type="text" ref={(val) => (focusTest.current[1] = val)}></input>
      <input type="text" ref={(val) => (focusTest.current[2] = val)}></input>
    </div>
  );
}

export default App;

 

그런데 버튼을 눌러도 아무 반응이 없다.

 

포커스가 가능한 태그는 여러 개가 있는데, 대표적으로 input, button, textarea 등이다.

그 외 <p> 태그<div> 태그등은 포커스가 불가능하다.

 

위의 태그도 포커싱이 되려면 tabIndex를 설정하면 된다.

        <p
          key={idx}
          ref={(val) => (pTagFocus.current[idx] = val)}
          tabIndex={idx}
        >
          {item}번째 p tag
        </p>

 

이제 <p> 태그로도 정상적으로 포커싱이 된다.

 

전체 코드는 다음과 같다.

import { useState, useEffect, useRef } from "react";

function App() {
  const focusTest = useRef([]);
  const pTagFocus = useRef([]); /* <p> 태그 포커스 용 useRef */

  const [temp, setTemp] = useState([]);
  const [number, setNumber] = useState("");

  useEffect(() => {
    let t = [];
    for (let i = 0; i < 100; i++) t.push(i);
    setTemp(t);
  }, []);

  return (
    <div className="App">
      <input value={number} onChange={(e) => setNumber(e.target.value)} />
      <button
        onClick={() => {
          pTagFocus.current[number].focus();
        }}
      >
        번째 p 태그로 이동
      </button>
      <button onClick={() => focusTest.current[0].focus()}>focus test</button>
      <button onClick={() => focusTest.current[1].focus()}>focus test2</button>
      <button onClick={() => focusTest.current[2].focus()}>focus test3</button>

      {temp.map((item, idx) => (
        <p
          key={idx}
          ref={(val) => (pTagFocus.current[idx] = val)}
          tabIndex={idx}
        >
          {item}번째 p tag
        </p>
      ))}
      <input type="text" ref={(val) => (focusTest.current[0] = val)}></input>
      <input type="text" ref={(val) => (focusTest.current[1] = val)}></input>
      <input type="text" ref={(val) => (focusTest.current[2] = val)}></input>
    </div>
  );
}

export default App;
반응형