[chapter 3] 간단한 To-Do 앱 만들며 리액트 익히기 - 3

서희찬·2022년 1월 31일
0

The Origin : React

목록 보기
5/17
post-thumbnail

JSX Key 속성 이해하기

만약 키 속성이 없으면 유니크한 키를 줘야한다고 에러가 뜬다

Key속성 이란 ?

리액트에서 요소의 리스트 나열시 Key 필수 !
키는 React 변경 추가 또는 제거된 항목 식별시 도움된다.
요소에 안정직인 아이디를 부여하려면 배열 내부의 요소에 키를 제공해야 한다!

리액트는 가상 돔을 이용해서 바뀐부분만 실제 돔에 적용!

바뀐 부분!만 실제 돔에 적용한다!
리스트에서 리스트 나열시 바뀐 부분만 찾을 때 어떻게 할까?

key를 이용해서 어떠한 부분이 바뀌었는지 인식 !

순차적

{/* before  */}
<ul>
  <li>1</li>
  <li>2</li>
</ul>
{/* after  */}
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
</ul>

이런식으로 3이 추가될때는 1,2뒤에 3을 넣장~ 하면서 바뀐 부분을 찾아서 넣는데
다음케이스에서 문제가 발생한다.

맨앞에다가 슝

{/* before  */}
<ul>
  <li>1</li>
  <li>2</li>
</ul>
{/* after  */}
<ul>
  <li>3</li>
  <li>1</li>
  <li>2</li>
</ul>

이럴땐 모든 요소가 새롭게 추가된거라 인식하고 모든 자식 앨리먼트를 새롭게 그려버린다!
그렇기에 key라는것을 줘서 1,2번을 새롭게! 추가된것이라 인식하지 않고 3번을 추가한 후 1,2번을 자리만 이동해준다

key는 유니크한 값

index를 사용하면 리스트 앞에 추가할 시 인덱스가 바뀌어 버리므로 비추천!
그렇기에 object를 위한 id값을 주는게 좋다

Filter 메소드를 사용해서 할 일 목록 지우기

X버튼 누르면 (클릭 이벤트가 발생하면) 할 일 목록을 지워버리는 함수를 호출해보자 !

btn에 onclick속성과 id를 전달해주는 함수를 호출해준다

<button style={this.btnStyle} onClick={()=>this.hanndleClick(data.id)}>X</button>

그 후

hanndleClick=(id)=>{
  //filter method를 사용해서 
  //id가 같은거를 필터링 해버리자 
  let newTodoData = this.todoData.filter(data=> data.id != id)
  console.log('newTodoData',newTodoData)
  //list의 id가 와서 데이터의 아이디가 아닌것만 트루를 반환해서 살린다 
}

함수를 작성해주면

이와같이 !
삭제버튼을 누르면 리스트에서 이 친구가 사라진 값을 반환해준다 !
다음 시간에 이제 화면에서 삭제되는걸 해보자

React State 란?

리액트에서 데이터가 변할 때 화면을 다시 렌더링 해주기 위해서는 React State를 사용해야한다.

그래서 그게 뭐에여?

컴포넌트의 렌더링 결과물에 영향을 주는 데이터를 갖고 있는 객체이다
(state가 변경되면 컴포넌트는 re-rendering된다. 또한 state는 컴포넌트안에서 관리된다)

todoData를 state에 넣자

state = { //객체로 state 생성 
  todoData : [ //배열안에 객체넣기 
    {
      id:"1",
      title:"공부하기",
      completed: true
    },
    {
      id:"2",
      title:"청소하기",
      completed: false 
    }
  ]
}

state를 이용해서 할 일 목록 삭제해보자

hanndleClick=(id)=>{
  //filter method를 사용해서 
  //id가 같은거를 필터링 해버리자 
  let newTodoData = this.state.todoData.filter(data=> data.id != id);
  console.log('newTodoData',newTodoData);
  //list의 id가 와서 데이터의 아이디가 아닌것만 트루를 반환해서 살린다 
  this.setState({todoData:newTodoData}); 
}

이런식으로 변경해주면!! 되는데
todoData가 state안에 들어갔으니
전부다 this.state.todoData형식으로 바꿔주자

그러고 나서 제거버튼을 눌러주면 사라지는것이 확인 가능하다 !

할 일 목록 추가하기


만들어보쟝

UI 부분

할일 목록 입력하는 부분

          <form style={{ display : 'flex'}}>
            <input 
              type="text" 
              name="value" 
              style={{flex:'10', padding:'5px'}} 
              placeholder="해야할 일 을 입력해주세요" 
              value=""/>

입력 버튼

            <input
              type="submit"
              value="입력"
              className="btn"
              style={{flex:'1'}}
            />
          </form>

이다

그러면 이러한 방식으로 UI부분이 완성되어이싿
이제 기능부분을 만들어보자

그런데..아직 입력이 불가하다.
그렇기에 input의 value에 state를 줘서 입력가능하게 만들어주자.

기능부분

onChange={this.handleChange}

를 인풋안에 추가해주고

handleChange =(e)=>{
  console.log('e',e);
}

함수를 작성해주면 변화가 생길때마다 이벤트를 받아서 아래와 같이 출력하게 된다.

handleChange =(e)=>{
  console.log('e',e.target.value);
}

로 하면
입력하고 있는 값들을 가져올수 있다.
그리고 setstate에 value:""를 추가해주고
input에 value를 {this.setstate.value}로 변경해주면 된다

handleChange =(e)=>{
 this.setState({value : e.target.value});
}

그리고 핸들체인지를 이르케 바꿔주면 된다
이제 입력 버튼 클릭했을때 목록 추가하고
입력란에 있던 글씨 지워주는 기능을 만들자

onSubmit={this.handleSumbit}

form에 이 코드를 추가해주고
핸들서밋함수를 만들어주자

handleSumbit = (e) =>{
  //form아ㄴ에 input전송시 페이지 리로드 막자 
  e.preventDefault();
  //새로운 할 일 데이터 
  let newTodo = {
    id : Date.now(), //유니크한 값 
    title: this.state.value,
    completed : false, 
  }
  //원래 있던 할 일에 새로운 일을 더하자 
  this.setState({todoData:[...this.state.todoData,newTodo]}) 
  // ... : 전개연산자 
  // 이미 있는거에 새로운거 더해주기 
}

아래와 같이 리로드를 막아주고
새로운 하일 데이터를 newTodo 객체를 통해서 만들어주고
키값은 유니크한 데이트값으로 줬다
그리고 그 객체를 todoData객체 안에 넣어주는것이다
... 전개연산자를 사용해서 모든 투두데이터를 들고오고 새로운 투두객체를 넣으면서 성공적으로 추가가 된다

this.setState({todoData:[...this.state.todoData,newTodo],value:""}); 

그후 벨류를 없애주면서 입력란안에 있던 글자를 지워주자

전개 연산자(Spread Operator)

특정 객체 또는 배열의 값을 다른 객체,배열로 복제하거나 옮기 때 사용한다!
생긴거는 ... 이렇게 생김 !

배열조합

const arr1 = [1,2,3];
const arr2 = [4,5,6];
const arr3 = [7,8,9];
const arrWrap = arr1.concat(arr2,arr3); 

console.log(arrWrap) // [1,2,3,4,5,6,7,8,9]

const arr1 = [1,2,3];
const arr2 = [4,5,6];
const arr3 = [7,8,9];
const arrWrap = [...arr1,...arr2,...arr3]
console.log(arrWrap) // [1,2,3,4,5,6,7,8,9]

const arr1 = [1,2,3]
const arr2 = [4,5]
arr1.push(...arr2)

뭐 이르케 사용할 수 있다

객체조합

const obj1={
  a:"A",
  b:"B"
}
const obj2={
  c:"C",
  d:"D"
}
const objWrap = {obj1,obj2};
console.log(objWrap); 
// {
//   obj1={
//     a:"A",
//     b:"B"
//   },
//   obj2={
//     c:"C",
//     d:"D"
//   }
// } 

이런식으로 객체 자체가 들어가는데
전개 연산자를 사용하면

const obj1={
  a:"A",
  b:"B"
}
const obj2={
  c:"C",
  d:"D"
}
const objWrap = {...obj1,...obj2};
// console.log(objWrap); 
// {
//   a:"A",
//   b:"B"
//   c:"C",
//   d:"D"
// }

객체 자체가 아닌 각각의 값이 할당된다

기존 배열 보존


이런식으로 원본 배열을 유지할 수 있다

마무리 된 일 표시하기(조건부 삼항 연산자)

UI 부분

getStyle = (completed) =>{
  return{
    padding : "10px",
    borderBottom:"1px #ccc dotted",
    textDecoration : completed ? "line-through" : "none",
  }
}

get스타일에 조건부 삼항 연사자를 써서 True면 Line-through 아니면 none이 되게해준다
이는 data.completed를 전달해줘서 이 함수가 프로퍼티로 받아서 사용하는것이다.

그러면 이렇게 그어진다 !

그런데 체크박스랑 연계되서는 아직안된다
이제 체크박스 클릭해서 완료 상태로 바꾸는것을 해보자

<input type="checkbox" defaultChecked={data.completed} onChange={()=>this.handleCompleteChange(data.id)}></input>

체크박스에 onChange속성에 handleCompleteChange를 주자

이를 통해 id를 전달해주고
함수에서 id를 받는데 data의 아이디와 일치하는것을 찾아 completed의 불리언값을 변경해주고 data를 반환해주는 map함수를 작성한다.
그리고 setState를 통해서 값을 반환해주면
된다 !!

그러면 성공적으로 작동한다

전체코드

import React, {Component} from "react";  // 리액트 라이브러리에서 컴포넌트 들고오기
import "./App.css";

export default class App extends Component{ // 컴포넌트를 사용할 수 있게 extends

  btnStyle = {
    color : "#fff",
    border : "none",
    padding : "5px 9px",
    borderRadius : "50%",
    cursor : "pointer",
    float : "right",
  }

//style 
getStyle = (completed) =>{
  return{
    padding : "10px",
    borderBottom:"1px #ccc dotted",
    textDecoration : completed ? "line-through" : "none",
  }
}

//할일 목록 삭제 함수 
hanndleClick=(id)=>{
  //filter method를 사용해서 
  //id가 같은거를 필터링 해버리자 
  let newTodoData = this.state.todoData.filter(data=> data.id != id);
  console.log('newTodoData',newTodoData);
  //list의 id가 와서 데이터의 아이디가 아닌것만 트루를 반환해서 살린다 
  this.setState({todoData:newTodoData}); 
}

handleChange =(e)=>{

  this.setState({value : e.target.value});

}
handleSumbit = (e) =>{
  //form아ㄴ에 input전송시 페이지 리로드 막자 
  e.preventDefault();
  //새로운 할 일 데이터 
  let newTodo = {
    id : Date.now(), //유니크한 값 
    title: this.state.value,
    completed : false, 
  }
  //원래 있던 할 일에 새로운 일을 더하자 
  this.setState({todoData:[...this.state.todoData,newTodo],value:""}); 
  // ... : 전개연산자 
  // 이미 있는거에 새로운거 더해주기 

  //입력란 안에 있던 글시 지워주기 설명 안하노 ㅋㅋ 
}


state = { //객체로 state 생성 
  todoData : [ //배열안에 객체넣기 
    {
      id:"1",
      title:"공부하기",
      completed: false
    },
    {
      id:"2",
      title:"청소하기",
      completed: false 
    }
  ],
  value:""
}


handleCompleteChange = (id) =>{
  let newTodoData = this.state.todoData.map(data=>{
    if(data.id === id){
      data.completed = !data.completed; 
    }
    return data; 
  })
  this.setState({todoData:newTodoData});
}

  render(){ // 변환한다 
    return( // 반환한다 
      // 컨테이너를 감싸고
      <div className="container">
        {/* div박스하나를 만든다. 투두블락 */}
        <div className="todoBlock">
          {/* 그리고 제목박스도  */}
          <div className="title">
            My To do List 
          </div>
          {/* 밑에서부터 할일 목록을 나열한다 */}
          {/* 반복형으로 나열 */}
          {this.state.todoData.map(data=>(
            // this는 클래스를 가리키고 클래스 안에 todoData라는 리스트객체를 가지고 와서 그 안에 데이터를 꺼내는데 map함수를 써서 꺼낸다
            // map은 객체별 요소를 data라는 변수로 정해주고 data객체 안에 id,completed,title을 가져온다
            // style같은경우도 겹치는 경우가 많으니 this를 사용해서 클래스 내에 만들어둔 스타일을 가지고 와서 사용한다
            // react에서는 반복되는 값들을 가지고올때 유니크한 값와 같은 key값을 줘야한다 
            <div style={this.getStyle(data.completed)} key={data.id}>
              <input type="checkbox" defaultChecked={data.completed} onChange={()=>this.handleCompleteChange(data.id)}></input>
              {data.title}
              <button style={this.btnStyle} onClick={()=>this.hanndleClick(data.id)}>X</button>
            </div>
          ))}
          <form style={{ display : 'flex'}} onSubmit={this.handleSumbit}>
            <input 
              type="text" 
              name="value" 
              style={{flex:'10', padding:'5px'}} 
              placeholder="해야할 일 을 입력해주세요" 
              value={this.setState.value}
              onChange={this.handleChange}
              />
            <input
              type="submit"
              value="입력"
              className="btn"
              style={{flex:'1'}}
            />
          </form>


        </div>
      </div>
      
    )
  }
}
profile
Carnegie Mellon University Robotics Institute | Research Associate | Developing For Our Lives, 세상에 기여하는 삶을 살고자 개발하고 있습니다

0개의 댓글

관련 채용 정보