Reactjs를 사용하다보면, state 를 변경해야하는 일이 빈번하다. 왜냐하면, 리액트는 상태를 감시하고 있다가 변경된 상태에 따라 컴포넌트가 리 렌더링되기 때문!!!
트위터 스프린트에서 아래와 같은 중요한 문제에 부딪혔다.
랜더링되는 트위터 메세지(단일 메세지: 객체)들은 배열의 형태로 있고 그런데, 새로운 메세지가 입력될 때(=기존 배열에 요소를 추가할 때) 왜 push() 가 아닌, concat() 을 사용할까? 그 이유에 대해 알아보기 전에 자바스크립트의 기본으로 돌아갈 필요가 있다.
React 컴포넌트에서는 state 또는 상위 컴포넌트에서 전달받은 props의 값이 변경될 때 리렌더링을 하게 된다. 객체나 배열과 같은 참조타입의 자료형의 경우, deep copy를 하지 않으면 내부의 값이 변경되었을지 몰라도, 레퍼런스가 가리키는 곳은 동일하기 때문에 똑같은 값으로 인식한다. 그러므로, 리 렌더링을 하지 않게 된다. 이런 문제 때문에, 기존 값을 '복사하여' 새로운 객체나 배열로 만들어주어야 한다. 그래야 래퍼런스 주소가 바뀐 것을 인지하고 리 렌더링을 하게 된다.
아래 메소드를 잘 사용하면 새로운 배열 객체 반환, 복제의 효과와 삭제의 효과를 나타낼 수 있다.
따라서 상태값을 변화시켜 리랜더링을 의도할 때에는 push(), pop(), shift(), unshift() 등의 가변성 있는 함수를 사용하면 안 된다. 사용하더라도 복사하고자 하는 배열을 spread나 slice등을 활용하여 복제를 해놓고 사용해야 한다.
const [newTweets, newTweetsAdded] = useState(dummyTweets);
--> dummyTweets이라는 배열이 초기값인 newTweets이라는 상태(state)를 선언한 것.
그리고 그 상태를 변경하기 위해 newTweetsAdded()
함수 내에 전달인자로 tweet.concat(newTweets)
즉, concat 함수를 사용하여 newTweets(초기값: dummyTweets)에다가 tweet이라는 객체를 포함시켰다.
즉, 여기서 newTweets
와 tweet.concat(newTweets)
은 서로 완벽히 다른 배열이기 때문에(서로 참조하지 않기 때문에) 리액트에서는 래퍼런스 주소가 바뀐것을 인지하고 리 랜더링을 해준다.
주석에도 썼다시피, 원래는 1. 여러 배열의 요소 중 객체의 id 값이delete를 클릭한 객체의 id와 일치하는 것의 인덱스를 찾고(findIndex 함수 사용) 2. 그 배열의 요소를 제거하려고 아래와 같이 splice 함수를 썼었으나,
const idx = newTweets.findIndex(i => i.id === el); //지우고자 하는 그 객체의 인덱스를 찾아서
newTweets.splice(idx, 1); //newTweets에서 해당 배열 지움
splice 함수는 가변성을 띄고 있어, 불변성 함수인 filter 함수를 사용하여 정정하였다.(그 객체의 id값과 일치하지 않는 것만 추림)
const restTweets = newTweets.filter(obj => obj.id !== el);
newTweetsAdded(restTweets);
트위터 추가 & 삭제를 함수의 불변성 개념과 연관시켜 공부해보았다.
오늘 공부 끝 ~