Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

리액트 정리

[React] 웹게임 3. 숫자야구 // ref 리액트에서 DOM에 직접적인 접근을 할 때 본문

리액트/웹게임

[React] 웹게임 3. 숫자야구 // ref 리액트에서 DOM에 직접적인 접근을 할 때

버그킴 2020. 1. 31. 14:48

컴포넌트의 메소드에서 컴포넌트의 태그에 직접 접근

 

https://velopert.com/1148

리액트 개발을 하다보면 DOM 에 직접적인 접근을 해야 할 때가 있습니다. 그럴 때는 ref 라는것을 사용합니다. 그런데 정확히 어떠한 상황에 DOM 에 직접적인 접근이 필요할까요? 필요한 상황은 다음과 같습니다.

  1. input / textarea 등에 포커스를 해야 할때
  2. 특정 DOM 의 크기를 가져와야 할 때
  3. 특정 DOM 에서 스크롤 위치를 가져오거나 설정을 해야 할 때
  4. 외부 라이브러리 (플레이어, 차트, 캐로절 등) 을 사용 할 때

https://www.zerocho.com/category/React/post/57833e23db96321500e401fe

Q. Reference 접근 할때 에서 ref={(ref) => { this.hide = ref; }} 대신 자바스크립트 DOM 접근 방식인 getElementById 를 사용하면 어떤 문제가 있나요?

-> 리엑트의 메서드를 쓸 수가 없게됩니다

 

 

1. Class 

  • 스트럭쳐
import React, { Component, createRef } from "react";
class NumberBaseball extends Component {
  ...
  this.inputRef.current.focus();
  ...
  inputRef = createRef();

  render() 
  return (
    <>
      <input ref={this.inputRef}/>	
    </>
  )

}

 

  • 전체코드
import React, { Component, createRef } from "react";
import Try from "./Try";

const getNumbers = () => {
  // 숫자 네개를 랜덤하게 뽑는 함수.
  const candidate = [1, 2, 3, 4, 5, 6, 7, 8, 9];
  const array = [];
  for (let i = 0; i < 4; i += 1) {
    const chosen = candidate.splice(Math.floor(Math.random() * (9 - i)), 1)[0];
    array.push(chosen);
  }
  return array;
};

class NumberBaseball extends Component {
  state = {
    result: "",
    value: "",
    answer: getNumbers(),
    tries: [] // 리액트에서는 push 쓰면 안돼요
  };

  onSubmitForm = e => {
    e.preventDefault();
    if (this.state.value === this.state.answer.join("")) {
      this.setState(prevState => {
        return {
          result: "홈런!",
          tries: [
            ...prevState.tries,
            { try: this.state.value, result: "홈런!" }
          ]
        };
      });
      alert("게임을 다시 시작합니다!");
      this.setState({
        value: "",
        answer: getNumbers(),
        tries: []
      });
      this.inputRef.current.focus();
    } else {
      // 답 틀렸으면
      const answerArray = this.state.value.split("").map(v => parseInt(v));
      let strike = 0;
      let ball = 0;
      if (this.state.tries.length >= 9) {
        // 10번 이상 틀렸을 때.
        this.setState({
          result: `10번 넘게 틀려서 실패! 답은 
          ${this.state.answer.join(",")} 였습니다!`
        });
        alert("게임을 다시 시작합니다!");
        this.setState({
          // 게임 초기화
          value: "",
          answer: getNumbers(),
          tries: []
        });
        this.inputRef.current.focus();
      } else {
        for (let i = 0; i < 4; i += 1) {
          // 몇 볼 몇 스트라이크인지 알려주기
          if (answerArray[i] === this.state.answer[i]) {
            strike += 1;
          } else if (this.state.answer.includes(answerArray[i])) {
            ball += 1;
          }
        }
        this.setState((prevState) => {
          return {
            tries: [
              ...prevState.tries,
              {
                try: this.state.value,
                result: `${strike} 스트라이크, ${ball} 볼입니다`
              }
            ],
            value: ""
          }; 
        });
        this.inputRef.current.focus();
      }
    }
    console.log(this.state.value);
  };

  onChangeInput = e => {
    this.setState({
      value: e.target.value
    });
  };

  inputRef = createRef();

  render() {
    return (
      <>
        <h1>{this.state.result} </h1>
        <form onSubmit={this.onSubmitForm}>
          <input
            ref={this.inputRef}
            maxLength={4}
            value={this.state.value}
            onChange={this.onChangeInput}
          />
          <button>Submit</button>
        </form>
        <div>시도: {this.state.tries.length} </div>
        <ul>
          {this.state.tries.map((v, i) => {
            return (
              // v 는 매번 새로 넣어주는 객체 {try: this.state.value, result:`${strike} 스트라이트, ${ball} 볼입니다`}이것
              <Try key={`${i + 1}차 시도: `} tryInfo={v} index={i} />
            );
          })}
        </ul>
      </>
    );
  }
}

export default NumberBaseball;

 

 

 

2. Hooks

 

  • 스트럭쳐
import React, { useState, useRef, memo }  from 'react';
inputEl.current.focus();

ref={inputEl}

 

  • 전체코드
import React, { useState, useRef, memo }  from 'react';
import Try from "./Try";

const getNumbers = () => { // 숫자 네개를 랜덤하게 뽑는 함수. 
  const candidate = [1,2,3,4,5,6,7,8,9];
  const array = [];
  for (let i = 0; i < 4; i += 1) {
    const chosen = candidate.splice(Math.floor(Math.random() * (9 - i)), 1)[0];
    array.push(chosen);
  }
  return array;
}


const NumberBaseball = memo(() => {
  const [answer, setAnswer] = useState(getNumbers());
  const [value, setValue] = useState("");
  const [result, setResult] = useState("");
  const [tries, setTries] = useState([]);
  const inputEl = useRef(null);


  const onSubmitForm = (e) => {
    e.preventDefault();
    if(value === answer.join('')) {
      setTries(prevState => 
        [...prevState, 
          {
            try: value, 
            result: "홈런!"
          } 
        ]);
      setResult("홈런!");
      alert("게임을 다시 시작합니다.");
      setValue("");
      setAnswer(getNumbers());
      setTries([]);
      inputEl.current.focus()
    } else {
      const answerArray = value.split("").map(v => parseInt(v));
      let strike = 0;
      let ball = 0;
      if(tries.length >= 9) {
        setResult(`10번 넘게 틀려서 실패. 답은 ${answer.join(",")} 였습니다!`);
        alert("게임을 다시 시작합니다. ");
        setValue("");
        setAnswer(getNumbers());
        setTries([]);
        inputEl.current.focus();
      } else  {
        for (let i = 0; i < 4; i += 1) {
          if (answerArray[i] === answer[i]) {
            console.log("strike", answerArray[i], answer[i]);
            strike += 1;
          } else if (answer.includes(answerArray[i])) {
            console.log("ball", answerArray[i], answer.indexOf(answerArray[i]));
            ball += 1;
          }
        }
        setTries(prevState => [
            ...prevState, 
            {
              try: value, 
              result: `${strike} 스트라이크, ${ball} 볼입니다`
            }
          ]);
        setValue("");
        inputEl.current.focus();
      }
    }
  };

  return (
    <>
      <h1>{result}</h1>
      <form onSubmit={onSubmitForm}>
        <input
          ref={inputEl}
          maxLength={4}
          value={value}
          onChange={e => setValue(e.target.value)}
        />
        <button>입력!</button>
      </form>
      <div>시도: {tries.length}</div>
      <ul>
        {tries.map((v, i) => (
          <Try key={`${i + 1}차 시도 : ${v.try}`} tryInfo={v} />
        ))}
      </ul>
    </>
  );
});
export default NumberBaseball;