티스토리 뷰

 

*피드백은 언제나 환영합니다🙌

 

오늘은 Tomorrow.js와 Someday.js 투두리스트 만드는 과정을 다뤄볼 예정이다. Tomorrow.js와 Someday.js Today.js의 투두리스트와 다르게 완료 체크, 수정 기능이 없어서 비교적 간단하게 만들 수 있었다.

 

 

 

 

 

Tomorrow.js


먼저 input에 들어갈 텍스트를 담기위해 state를 하나 생성했다.

let [text, setText] = useState("");

 

 

그리고 onChange 속성을 이용해 input에 들어가 있는 값을 실시간으로 받아올 수 있도록 했다.

<input 
    type="text"
    className='insertBox' 
    onChange={(e)=>{
      setText(e.target.value);
    }}
    value={text}
    placeholder='✔ 할 일 추가 (Press Enter)' 
/>

 

 

제출 버튼을 없앴기 때문에 엔터를 누르면 할 일 목록이 등록되도록 post() 함수를 추가했다. 그런데 왜 기존 list의 카피본을 만들게 됐는지 정확한 이유가 생각나지 않았다. 검색 ㄱㄱ

function post (e) {
    const copyList = [...list];
    copyList.push(text);
    setList(copyList);
    setText('');
};

 

 

리액트와 불변성의 관계


리액트는 state라는 메모리가 변했다고 판단되면 변경된 부분을 리렌더링 해준다. 이때 주의할 점은 state가 참조 타입(Object, Array, funtion)의 데이터라면, 만약 배열이 값으로 할당 된 state를 업데이트 하려는 경우 새로운 배열을 반환하는 방식을 사용해야 한다.

예를 들어, push 메서드는 원본 배열을 변경하게 되므로 setState 는 이전 state와 비교했을 때, 변경점이 없는 것으로 판단해 해당 사항에 대해 다시 렌더를 실행하지 않는다. 그래서 새 배열을 반환하는 방법을 사용해야 하는데, 대표적으로 concat 또는 ... spread 연산자를 사용하는 방법이 있다.

 

결론적으로 array나 object 자료를 다룰 때는 원본 데이터를 직접 조작하기보다 기본값을 보존해주는 식으로 코드를 짜야한다는 말이다. 

 

예전에 원시타입과 참조타입, 메모리 힙 등에 대해 내가 정리했던 글이 있다.

https://codingtoddlerr.tistory.com/28

 

이제는 메모리 힙이 뭐고 원시타입이 무엇인지 알게됐다. 그때는 도통 무슨 말인지 이해가 안됐는데 그래도 알아야 한다니까.. 일단 정리해놓으면 도움이 되겠지.. 했는데😥 리액트를 공부하면서 직접 코드를 짜보니까 와닿는다.

 

 

 

 

 

다음으로 post() 함수를 만들고 onKeyUp 속성으로 엔터를 누르면 할 일 목록에 추가 될 수 있도록 했다. 여기서도 Today.js 에서 했던 것과 동일하게 텍스트 길이가 0보다 클 때 제출 될 수 있게 했다.

function post (e) {
    const copyList = [...list];
    copyList.push(text);
    setList(copyList);
    setText('');
};

.
.
.

<div className='textList'>
<p>할 일</p>
<div className='inputButton'>
  <input 
    type="text"
    className='insertBox' 
    onChange={(e)=>{
      setText(e.target.value);
    }}
    onKeyUp={(e)=> { 
      if(e.key === 'Enter') {
        if(e.target.value.length > 0) {
            post()
          }
      } 
    }}
    value={text}
    placeholder='✔ 할 일 추가 (Press Enter)' 
    />
</div>
</div>

 

 

이제 똑같은 html을 생성하기 위해 자바스크립트 기본함수인 map() 함수를 사용했다. 파라미터로 textArr를 넣어주고 각각의 아이템을 삭제할 수 있도록 splice() 메소드를 썼다.

 

splice() 함수를 사용하여 배열 요소를 삭제

첫 번째 인수는 제거할 배열 요소의 위치(인덱스)를 설정한다.
두 번째 인수는 제거할 배열 요소의 개수를 설정한다.
즉, 설정한 배열 요소의 위치부터 특정 개수만큼 배열의 요소를 삭제한다.
{
    list.map((textArr, i) => 
        <div className='todolistitemStyle'>
          <div className='checkboxAndcontent'>
            <p>{textArr}</p>
          </div>
          <div className='removeboxDiv'>
            <FaRegTrashAlt className='removebox' onClick={()=>{
              let copy = [...list];
              copy.splice(i, 1);
              setList(copy);
            }}/>
          </div>
        </div>
      )
}

 

 

 

마지막으로 localStorage를 사용하여 새로고침이나 초기화를 해도 데이터가 날아가지 않도록 만들었다. localStorage에 대한 부분은 저번 글에서 다뤘기때문에 이번에는 간략하게 넘어갈 것이다.

let [list, setList] = useState(()=> {
        const saved =  window.localStorage.getItem("tomorrowInLocal");
        if(saved !== null) {
          return JSON.parse(saved);
        } else {
          return [];
        }
    });
    
useEffect(()=>{
  localStorage.setItem("tomorrowInLocal", JSON.stringify(list));
}, [list]);

 

 

 

 

여기까지 하면 Tomorrow.js 는 끝인줄 알았지만 역시 오늘도 에러가 떴다

Warning: Each child in a list should have a unique "key" prop.

 

리액트는 key prop을 사용하여 컴포넌트와 DOM 요소 간의 관계를 생성한다. 리액트 라이브러리는 이 관계를 이용해 컴포넌트 리렌더링 여부를 결정한다. 따라서 불필요한 리렌더링을 방지하기 위해서는 각 자식 컴포넌트마다 독립적인 key값을 넣어줘야 한다.

 

해결방법은 배열로 map 함수를 사용해 JSX 리스트를 구현할 때  key prop을 자식 컴포넌트마다 넣어주는 것이다.

자바스크립트의 배열은 정적이지 않아서 배열의 길이나 원소 등이 변할 수 있다.

 

따라서 배열의 index를 key prop으로 사용하는 것은 지양해야 한다. 이유는 배열의 원소 순서가 바뀌면 index도 바뀌고 컴포넌트마다 고유해야 하는 key 값도 같이 바뀌기 때문이다. 이렇게 되면 리액트는 리렌더링 해야 하는 컴포넌트를 헷갈려 잘못된 컴포넌트를 리렌더링 할 수 있다.

{
    list.map((textArr, i) => 
        <div key={textArr} className='todolistitemStyle'>
          <div className='checkboxAndcontent'>
            <p>{textArr}</p>
            </div>
            <div className='removeboxDiv'>
              <FaRegTrashAlt className='removebox' onClick={()=>{
                let copy2 = [...list];
                copy2.splice(i, 1);
                setList(copy2);
              }}/>
            </div>
        </div>
      )
  }

 

 

이렇게 Tomorrow 컴포넌트도 끝이 났다. Someday 컴포넌트는 Tomorrow 컴포넌트와 크게 다른 것이 없다. 거의 복붙 수준인데.. 굳이 컴포넌트를 새로 만들지 않고 할 수 있는 방법 없나..? 

 

다음 글에서는 휴식 탭을 다뤄볼 예정이다.

이제 슬슬 끝이 보인다. 끝까지 화이팅💪

 

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
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
글 보관함