본문 바로가기
학원에서 배운 것/React

KDT 5th 웹개발자 입문 수업 37일차

by 쿠리의일상 2023. 3. 28.

배열을 state 로 사용하면..?

export default function StateProblem() {
  let [state, setState]  = useState([0]);
  console.log('initial state : ',state);

  return (
    <div>
      {state}
      <br/>
      <button
        onClick={() => {
          state[0] = 1;
          setState(state);
          console.log('after state : ',state)
        }}
      >
        1로 만들기
      </button>
    </div>
  )
}

실제 콘솔창에 0 -> 1로 변화했는데, html 상에선 변경이 안되는 이유

 

원시 타입의 경우 값을 저장할 때 그 값 자체를 저장하지만,

const location1 = 'korea';
const location2 = 'korea';

location1 === location2 // true

객체나 배열(비원시 타입)은 기본적으로 값을 저장할 때 메모리 주소를 저장해주므로

const location1 = { country : 'korea' }
const location2 = { country : 'korea' }

location1 === location2 // false

 

해당 특성을 활용하여,

setState() 로 배열이나 객체의 값을 변화시켜도 다른 배열이나 객체값이 변하는 게 아니므로, 메모리 주소값은 변하지 않았다고 리액트가 여기기 때문에 결국 상태 변화가 일어나지 않았다고 여기므로 리렌더링이 일어나지 않는 것이다.

 

--> 이는 새로운 메모리 주소값을 전달하면 상태 변화가 일어났다고 여기게 된다! => 리렌더링이 일어남

export default function StateProblem() {
  let [state, setState]  = useState([0]);
  console.log('initial state : ',state);

  return (
    <div>
      {state}
      <br/>
      <button
        onClick={() => {
          state[0] = 1;
          const copyArr = [...state];
          setState(copyArr);
          console.log('after state : ',state)
        }}
      >
        1로 만들기
      </button>
    </div>
  )
}

배열이나 객체를 state 에 사용하기 위해선 해당 특성을 이해하고 있어야 한다.

  • 자주 값이 변경 되고, 변경 사항을 바로 보여줘야할 때 state 로 선언
  • 배열 또는 객체 보다는 하나의 원시값으로 사용하는 것이 좋다

 

 

컴포넌트 활용하기

컴포넌트를 쓰면 코드를 크게 줄일 수 있으며 재사용도 편리함

모달에 내용이 추가되어도 원본 파일 하나만 바꾸면 모든 모달이 변하므로 이전처럼 각각의 코드에 가서 수정할 필요가 없다.

 

모양은 동일하나 내용이 다른 경우

Props 

부모 컴포넌트에서 자식 컴포넌트에 원하는 데이터를 보내는 방법

props 라는 객체로 전달

 

자식 컴포넌트의 매개변수 부분에 비 구조화 할당 문법인 객체형식({ })으로 부모 컴포넌트로부터 받아올 props명을 적어준다.

부모 컴포넌트는 속성을 넣어주듯 키=값 형식으로 props를 넣어준다.

export default function PropsHeader({ text }) {
  return (
    <div>
      {text}
    </div>
  )
}
function App() {
  return (
    <div className="App">
      <PropsHeader text="안녕하세요"/>
    </div>
  );
}

또는 자식 컴포넌트 매개변수를 props 객체 자체로 받아줘도 된다.

export default function MultiProps(props) {
  return (
    <div>
      <h1>{props.userId} 님 반갑습니다.</h1>
      <a href={props.href}>{props.text}</a>
    </div>
  )
}

 

클래스형 컴포넌트에서 props

클래스형 컴포넌트인 만큼 기존 props 로 접근하던 것을 this.props 로 접근할 수 있다.

클래스이기 때문에 매개변수를 따로 받지 않는다.

this.props 를 쓰면 코드가 길어지므로 보통 구조 분해 할당으로 변수로 만들어서 사용한다.

 

 

리액트에서 배열 활용하기

컴포넌트에 props 를 전달할 때 백엔드에서 데이터를 받아오게 되고 받은 데이터를 props에 전달하는 형태를 많이 사용한다.

React 에서 배열 데이터를 그려줄 때 배열 함수인 map 사용

export default function List() {
  const todo = [
    {
      title:"리액트 공부하기",
      content:"state 사용법 익히기",
    },
    {
      title :"코테 문제 풀기",
      content:"Lv0 정복 가자",
    }
  ]

  return (
    <div>
      <Modal/>
      <h1>오늘 해야할 일</h1>
      <hr/>
      {todo.map((e) => {
        return <ListChild title={e.title} content={e.content}/>
      })}
      <Modal/>
    </div>
  )
}

하지만 이런 경우 warning 이 발생한다.

그 이유는 리액트에서 map으로 배열 데이터를 그려줄 때 해당 요소에 unique 한 key 값을 props를 주지 않았기 때문이다.

리액트에서 map으로 그려진 엘리먼트 중에서 어떤 것을 컨트롤(변경,추가,삭제) 할 지 구분을 해야하므로 반드시 key값에 unique한 값으로 넣어줘야 한다.

 

map이 받아주는 콜백함수는 1.value값 2.index값 순으로 받아줄 수 있으므로 구별이 가능한 key 값을 index를 사용하여 부여해준다.

  {todo.map((e, idx) => {
        return <ListChild title={e.title} content={e.content} key={idx}/>
      })}

 

배열이나 객체를 props에 전달하고 처리하기

export default function CustomList({arr}) {
  return (
    <ul>
      {arr.map((e, idx) => <li key={idx}>{e}</li>)}
    </ul>
  )
}
export default function CustomObj(props) {
  const {name, age, nickname} = props.obj;
  return (
    <div>
      <h2>이름 : {name}</h2>
      <p>나이 : {age}</p>
      <h3>별명 : {nickname}</h3>
    </div>
  )
}

만약 props 로 배열 또는 객체 데이터가 들어오지 않는다면?

 

1. if문 사용하기

props 가 들어왔는지 처리

다만 if문은 JSX 문법 내부에서는 사용이 불가능 -> 삼항 연산자 사용해야 함

export default function CustomObj(props) {
  if(!props.obj) return (<div>데이터가 없습니다.</div>)
  
  const {name, age, nickname} = props.obj;
  return (
    <div>
      <h2>이름 : {name}</h2>
      <p>나이 : {age}</p>
      <h3>별명 : {nickname}</h3>
    </div>
  )
}

또는 return 밖에서 사용 하고 상황에 따라 다른 return 을 주는 방식으로 사용해준다.

 

 

2. Prototype chaining 에 ? 사용하기

해당 객체 또는 key값이 들어올지에 대한 확신이 없을 때에는 ?를 사용해서 에러를 막을 수 있다.

 

참조한 key에 값이 없어서 undefined 를 띄우는건 에러가 발생하지 않지만, undefined 값에서 뭔가 처리를 하려고 하면 에러가 발생한다.

 

즉, 값이 들어올지 확실하지 않은 key 뒤에 ?를 붙여주면 해당 key가 들어왔을 때에만 뒤의 체인을 참조하고, undefined 면 뒤의 부분은 실행되지 않는다. if 문보다 처리가 깔끔하지 않지만, 에러 자체는 막을 수 있다.

데이터를 받아오는 곳에 대한 확신이 없을 때 사용하면 좋다.

export default function CustomList({arr}) {
  return (
    <ul>
      {arr?.map((e, idx) => <li key={idx}>{e}</li>)}
    </ul>
  )
}

 

 

import React, { useState } from 'react'

export default function Profile(props) {
  const objArr = props.objArr;
  const [count, setCount] = useState(0);
  const obj = objArr[count];

  const changeProfile = () => {
    if(objArr.length - 1 === count) setCount((cur) => cur = 0);
    else setCount(count + 1);
  }
  
  return (
    <ul>
      <li>이름 : {obj?.name}</li>
      <li>나이 : {obj?.age}</li>
      <li>별명 : {obj?.nickname}</li>
      <button onClick={changeProfile}>프로필 변경하기</button>
    </ul>
  )
}