TIL 22-03-18 (1)

thisisyjin·2022년 3월 18일
0

TIL 📝

목록 보기
3/113
post-thumbnail

React.js

Today I Learned ... react.js

🙋‍♂️ React.js Lecture

🙋‍ My Dev Blog


React 반복문(loop)

배열 반복문

// 🔻 이차원 배열의 경우
return (
    <>
      <ul>
        {[
          ["사과", "맛있다"],
          ["바나나", "맛없다"],
          ["포도", "시다"],
          ["배", "달다"],
        ].map(v => {
          return (
            <li>
              <b>{v[0]}</b>
              - {v[1]}
            </li>
          );
        })}
      </ul>
    </>
  );
// 🔻 배열 안 객체의 경우 (보통 이렇게 씀)
return (
    <>
      <ul>
        {[
          { fruit: "사과", taste: "맛있다" },
          { fruit: "바나나", taste: "맛없다" },
          { fruit: "포도", taste: "시다" },
          { fruit: "배", taste: "달다" },
        ].map(v => {
          return (
            <li>
              <b>{v.fruit}</b>
              - {v.taste}
            </li>
          );
        })}
      </ul>
    </>
  );

🙋‍♂️ TIP 1 - key 지정

  • map 사용시 key를 반드시 지정해줘야 한다.
  • key는 중복되지 않고 고유한 값이여야 한다.
  • index를 key로 사용할수도 있지만, 성능최적화시 문제가 발생한다.
//[배열]
  .map(v => {
          return (
            <li key={v.fruit}>
              <b>{v.fruit}</b>
              {v.taste}
            </li>
          );

🙋‍♂️ TIP 2 - arrow function에서 return 생략

  • Arrow Function에서는 { } 를 생략하면 return을 생략해도 좋다.
  • ()안에 적어준 것을 바로 return 한다.
.map(v => (
          <li key={v.fruit}>
            <b>{v.fruit}</b>
            {v.taste}
          </li>
        ))

위 코드는 제대로 동작하지만, 가독성이 좋지 않다고 생각할 수 있다.
🔻
props를 이용하면 된다!


props를 이용한 반복문


📝 FruitsInfo.jsx


import React from "react";
import Try from "./Try";

const FruitsInfo = props => {
  const fruits = [
    { fruit: "사과", taste: "맛있다" },
    { fruit: "바나나", taste: "맛없다" },
    { fruit: "포도", taste: "시다" },
    { fruit: "배", taste: "달다" },
  ];

  return (
    <>
      <ul>
        {fruits.map((v, i) => (
          <Try el={v} index={i} />
        ))}
      </ul>
    </>
  );
};

export default FruitsInfo;

하위 컴포넌트인 Try에 elindex라는 props를 넘겨주고,

<Try el={v} index={i} />

반복문의 v를 그대로 el로 넘기고, i는 index로 넘겨준 것이다.


📝 Try.jsx

import React from "react";

function Try({ el, index}) {
  return (
    <li key={el.fruit}>
      <b>{el.fruit}</b>{el.taste} - {index}
      <div>컨텐츠1</div>
      <div>컨텐츠2</div>
      <div>컨텐츠3</div>
      <div>컨텐츠4</div>
    </li>
  );
}

export default Try;

여기서는 props.el과 props.index를 각각 {el, index} 로 받고나서,
el.fruitel.taste를 필요한 곳에 작성해주면 된다.

🙋‍♂️ 이렇게 Try 컴포넌트로 따로 분리했는지?

  • 가독성 / 재사용성 / 성능 최적화 용이

🔻 Class Component로 작성시

return (
      <li key={this.props.el.fruit}>
        <b>{this.props.el.fruit}</b>{this.props.el.taste} - {this.props.index}
        <div>컨텐츠1</div>
        <div>컨텐츠2</div>
        <div>컨텐츠3</div>
        <div>컨텐츠4</div>
      </li>
    );

this.props.el과 같이 작성해야한다.


💡 탑다운 VS 바텀업 방식

탑다운 (Top-Down)

큰 컴포넌트(상위 컴포넌트)에서 작성 후,
하위 컴포넌트로 기존의 값을 props로 보내는 등
컴포넌트를 분리하는 것. (위에서 했던 방식)

바텀업 (Bottom-Up)

작은 컴포넌트부터 만들기 시작함.
React가 숙련되면 Bottom-Up 방식으로 제작하게 됨.


🙋‍♂️ TIP 3 - props가 있다면?
= 부모 컴포넌트가 있다!

  • props가 선언되는 순간 컴포넌트끼리 부모-자식 관계가 성립되는 것.
  • 점점 부모-자식 관계가 확장되면 복잡해진다.
  • 이때는 context, redux 등을 사용한다.

🙋‍♂️ TIP 4 - React에서 주석(Comment)처리는?

  • javaScript에서는 // 주석 이지만,
  • React 에서는 {/* 주석 */} 으로 작성해야함.
  • 반드시 중괄호(curlyBrackets)로 감싸줌.

🙋‍♂️ TIP 5 - Class Component에서 메소드 선언시 Arrow Function으로 작성해야 함!

onInputChange = (e) => {
  this.setState({
    value: e.target.value
  });}
  • ()=> {} 로 작성해야 this가 바인딩된다.
  • 화살표함수는 bind(this)를 자동으로 해줌!
  • 만약 this.state나 this값들이 필요 없다면,
    일반 function으로 적어도 괜찮다.
  • 🔻 constructor을 사용하는 방식도 있긴 함.
class CompName extends Component {
  constructor(props) {
    super(props);
    this.state = { };
    this.onInputChange = this.onInputChange.bind(this); 
  }
  • cf> render()안에서는 Arrow Function으로 안써도 된다. (extends React.Component에서 처리해줌.)

숫자야구(Bulls and cows) 예제

이 포스팅에 따로 작성해두었다.

💎 key point

array인 state에 직접 push하면 안되고,
(원본의 값만 달라진다)

...(spread Operator)새 배열을 선언해야 한다.

  • refernce가 달라져서 array가 값이 달라졌음을 React가 알아채도록.

🙋‍♂️ strike, ball 판단 Logic

✅ 방법 1

// 반복 변수 하나로 OK.
for (let i = 0; i < 4; i++) {
          if (valueArr[i] === answer[i]) {
            strike++;
          } else if (answer.includes(valueArr[i])) { // 포함하는지
            ball++;
          }

✅ 방법 2

// 반복 변수(i,j) 두개 필요
 for (let i = 0; i < 4; i++) {
          for (let j = 0; j < 4; j++) {
            if (valueArr[i] === answer[j]) {
              if (i === j) {
                strike++;
              } else {
                ball++;
              }
            }
          }

🔻 [참고] if-else문의 순서!

  • 내가 작성했던 코드
    • 나는 먼저 횟수가 10회미만인지를 검사했었는데,
      이는 10회째에 맞춰도 게임오버가 된다.
if (tries.length >= 9) { // tries.length가 9면 > 이전 로그가 9개니까 지금이 10회째 시도임!
      setResult(`게임오버! 정답은 ${answer.join("")} 였습니다.`);
      setValue("");
      setTries([]);
      setAnswer(getNumbers());
    } else {
      if (value == answer.join("")) {
        // 정답이면 
      } else {
        // 오답이면 - strike, ball 
  • 수정 코드
    • 정답인지 아닌지 먼저 체크해야한다.
if (value === answer.join("")) {
      // 정답이면
    } else { // 오답이면
      if (tries.length >= 9) {
         // 횟수 초과시 - gameover
      } else {
        for (let i = 0; i < 4; i++) {
          // strike, ball 

📝 다시시작 버튼 만들기

게임오버시

  • 10회 초과시 게임오버 - 다시시작 버튼이 나타남
  • 정답 맞춰도 게임오버 - 다시시작 버튼이 나타남
  • 다시시작 버튼 클릭시
    • value, tries, answer, result 전부 리셋
    • ❗ 다시 버튼 안보이게
  • 버튼은 삼항연산자로 렌더링 여부 결정
import React from "react";
import { useState, useRef } from "react";

function getNumbers() {
  const candidate = [1, 2, 3, 4, 5, 6, 7, 8, 9];
  const answerArr = [];
  for (let i = 0; i < 4; i++) {
    const chosen = candidate.splice(Math.floor(Math.random() * (9 - i)), 1)[0];

    answerArr.push(chosen);
  }
  return answerArr; // = answer = [2,5,6,1]
}

const BullsAndCows = () => {
  const [answer, setAnswer] = useState(getNumbers());
  const [value, setValue] = useState("");
  const [result, setResult] = useState("");
  const [tries, setTries] = useState([]); // push쓰면 원본 바뀌므로 ...로 추가해주기
  const [gameover, setGameover] = useState(false);

  let inputRef = useRef(null);

  const onSubmitForm = e => {
    e.preventDefault();
    let strike = 0;
    let ball = 0;
    const valueArr = value.split("").map(v => parseInt(v));
    console.log(answer, valueArr);

    if (value === answer.join("")) {
      setResult("홈런! 정답입니다.");
      setTries([...tries, { try: value, result: "홈런! 정답입니다." }]);
      setValue("");
      setGameover(true);
    } else {
      if (tries.length >= 9) {
        setResult(`게임오버! 정답은 ${answer.join("")} 였습니다.`);
        setTries([...tries, { try: value, result: `게임오버!` }]);
        setGameover(true);
      } else {
        for (let i = 0; i < 4; i++) {
          if (answer[i] === valueArr[i]) {
            strike++;
          } else if (answer.includes(valueArr[i])) {
            ball++;
          }
        }
        setValue("");
        setResult(`${strike}스트라이크 ${ball}`);
        setTries([
          ...tries,
          { try: value, result: `${strike}스트라이크 ${ball}` },
        ]);
      }
    }
  };

  const onChangeInput = e => {
    setValue(e.target.value);
  };

  const onClickBtn = e => {
    alert("게임을 다시 시작합니다!");
    setResult("");
    setValue("");
    setTries([]);
    setAnswer(getNumbers());
    setGameover(false);
  };

  return (
    <>
      <div>
        <h1>🎲숫자야구🏏</h1>
        <p>남은 횟수: {10 - tries.length}</p>
        <form onSubmit={onSubmitForm}>
          <input
            required
            type="number"
            maxLength={4}
            value={value}
            placeholder="숫자를 맞혀보세요"
            onChange={onChangeInput}
            ref={inputRef}
          />
          <button>입력</button>
        </form>
      </div>
      <div>{result}</div>
      <ul>
        🔎 로그
        {tries.map(el => (
          <li key={el.try}>
            {el.try} : {el.result}
          </li>
        ))}
      </ul>
      {gameover ? (
        <button type="button" onClick={onClickBtn}>
          다시시작
        </button>
      ) : null}
    </>
  );
};

export default BullsAndCows;

📃 details

// 렌더링 설정
{gameover ? (
        <button type="button" onClick={onClickBtn}>
          다시시작
        </button>
      ) : null}
// state 설정
 const [gameover, setGameover] = useState(false);
if (value === answer.join("")) {
      // 정답시 - 새게임
      setGameover(true);
    } else {
      if (tries.length >= 9) {
        // 횟수 초과시 - 게임오버 - 새게임
        setGameover(true);
// 🔻 onClickBtn 함수
  const onClickBtn = e => {
    alert("게임을 다시 시작합니다!");
    setResult("");
    setValue("");
    setTries([]);
    setAnswer(getNumbers());
    setGameover(false);
  };

🙋‍♂️하위 컴포넌트로 분리하기


🔻 BullsAndCows.jsx

  • tries의 각 요소를 tryInfo라는 prop으로 보냄.
// tries.map 부분
<ul>
        🔎 로그
        {tries.map((v, index) => (
          <Try key={`${index}차시도`} tryInfo={v} />
        ))}
</ul>

🔻 Try.jsx

  • props를 받아와서 사용
import React from "react";

const Try = ({ tryInfo, index }) => {
  return (
    <li key={`${index}차 시도:`}>
      {tryInfo.try} : {tryInfo.result}
    </li>
  );
};

export default Try;

참고
원래 index를 key로 사용하면 안되지만,
이런 경우에는 사용해도 상관 없음.

profile
기억은 한계가 있지만, 기록은 한계가 없다.

0개의 댓글