[React 에러 일지] Cannot read properties of undefined...

nemo·2021년 12월 27일
67

에러 일지

목록 보기
1/26

자바스크립트 에러 Top 10 🚫

Top 10 JavaScript errors from 1000+ projects (and how to avoid them)

  1. Uncaught TypeError: Cannot read property ...
  2. TypeError: ‘undefined’ is not an object ...
  3. TypeError: null is not an object ...
  4. (unknown): Script error
  5. TypeError: Object doesn’t support property
  6. TypeError: ‘undefined’ is not a function
  7. Uncaught RangeError
  8. TypeError: Cannot read property ‘length’
  9. Uncaught TypeError: Cannot set property
  10. ReferenceError: event is not defined


Cannot read properties of undefined

리액트에서 가장 자주 보는 오류 메시지이다. 이 오류는 값이 정의되지 않아 읽을 수 없을 때 발생한다.

Uncaught TypeError: Cannot read properties of undefined (reading 'classList')

원인

위와 같은 에러는 React뿐만 아니라 Angular, Vue 프레임워크에서도 자주 발생한다.

여기서 알아야할 사실은 state가 비동기적이며 처음 렌더링(마운팅)하기도 전에 동작한다는 것이다. 이 때의 state는 정의되지 않았기 때문에 당연히 undefined다.
때문에 값을 읽을 수 없다는 에러가 출력되는 것이다.



해결 방법

1. State에게 들어올 값의 타입을 미리 알려주자.

State에 미리 값의 타입을 알려주면 undefined를 방지할 수 있다. 그러면 state는 "음, 배열이 들어오겠네? 그냥 아직 데이터가 들어오지 않았을 뿐이군?" 하면서 에러를 뿜어내지 않는다.


function Component() {
  // useState 괄호 안이 비어있는 건 비추 
  const [date, setData] = useState();
  
  // useState에 미리 값의 타입을 알려주자
  const [date, setData] = useState([]);
  // 혹은
  const [date, setData] = useState({});
   // 또는
  const [date, setData] = useState("");
   // 타입을 모르겠다면
  const [date, setData] = useState(null);

  return (
   ...
  )
}


2. && 연산자와 조건문을 적극 활용하자.

&& 연산자를 사용하면 && 앞뒤로 false 값을 찾고, false가 없다면 뒤에 있는 값을 출력한다. 앞, 뒤 모두 true면 뒤에 있는 값을 출력해주는 원리이며 이를 응용하면 되겠다.
조건식에 false가 있는 경우 null이 되고 렌더링하지 않으며, 렌더링하지 않으니 오류도 출력되지 않는다.



📌 Case 1. 컴포넌트에서 값이 없다며 오류가 나는 경우

data라는 state에 아직 값이 없는 경우, && 조건식은 false이며 <p>Hello!</p>는 렌더링되지 않는다.


function Component() {
  const [data, setData] = useState("");

  return (
    <div class="App">
      {
        data && <p>Hello!</p>
      }		
    </div>
  )
}


📌 Case 2. useEffect() 100개 사용했는데도 오류가 나서 화나는 경우

useEffect를 사용해서 .img가 그려지는 작업이 끝난 후, 0번째 .img를 콘솔에 출력하고 싶었지만 오류가 났다.

const [img, setImg] = document.querySelectorAll('.img');

useEffect(() => {
  console.log(img[0]);
}, [ img ]);

return (
  <div>
    {
      data.map((item, idx) => {
        return <div className="img" key={idx}></div>
      })
    }
  </div>
) 

Scroll.js:87 Uncaught TypeError: Cannot read properties of undefined

.img가 그려지기 전에는 img라는 state가 undefined이기 때문이다. 이 경우에 if 조건문을 사용하여 img 값이 존재하는지 확인만 해주면 쉽게 해결된다.

const [img, setImg] = document.querySelectorAll('.img');

useEffect(() => {
  if (img) {
  	console.log(img[0]);
  }
}, [ img ]);

return (
  <div>
    {
      data.map((item, idx) => {
        return <div className="img" key={idx}></div>
      })
    }
  </div>
) 

img state에 값이 있는 경우(true)에만 콘솔이 출력된다.



3. Optional Chaining ?(물음표) 연산자 사용해보기

마침표 앞에 물음표를 사용하면 유효한 값이 없을 경우에는 undefined를 반환하고, 값이 있을 때에만 마침표 뒤를 실행하기 때문에 오류를 방지할 수 있다.


const rolling () => {
	const $ele = document.querySelector(".ele");
  	
  	// 지금 $ele가 있어? 있으면 margin top 적용해줘. 없음 말고. 에러는 사절임.
	$ele?.getElementsByClassName.marginTop = "2em";
}

return (
  <div className"App">
    <div className="ele"></div>
  </div>
)


4. DOM 엘리먼트 조작은 ref hook을 사용해보자.

DOM 요소에 접근해야 하는데 위 방법들로는 해결되지 않았다면 ref를 사용해보자.

ref를 사용하면 리렌더링 시에도 레퍼런스 값이 유지된다. 또한 레퍼런스 값이 업데이트 되더라도 리렌더링되지 않는다.

리액트 공식 문서에는 ref 사용을 지양해야한다고 명시되어 있지만, 직접 DOM 요소를 조작해야하는 상황이 있을 때에는 ref를 사용하면 쉽게 해결할 수 있다.

state와 ref 차이점

  • state의 변화는 리렌더링을 트리거하지만 ref는 아니다.
  • state는 비동기적이며 렌더링 완료되었을 때 사용 가능하지만, ref는 동기적이라 업데이트되면 바로 사용할 수 있다.
import { useRef } from 'react';

const LabPage = () => {
  const anchorRef = useRef(null);
	
  const anchorHandler = () => {
  	const anchor = anchorRef.current;
    
    ...
  }
  
  return (
    <div className="anchor" ref={ anchorRef }> 
		...
    </div>
  )
}

2개의 댓글

comment-user-thumbnail
2022년 7월 31일

좋은글 감사합니다 퍼가도 될까요?

1개의 답글