Programming/React

React 할 일 목록 만들기 ( TodoList ) 추가,수정,삭제,검색

Chanho. 2020. 11. 22. 23:52
반응형

실습은 npx react-creat-app [projectname] 으로 실습환경 구축

기능은 추가, 수정, 삭제, 검색을 만들도록 하겠습니다.

프로젝트 구조

실습코드

app.js

import React, { Component } from 'react';
import ToDoForm from './component/ToDoForm';
import ToDoList from './component/ToDoList';
import './App.css';
class App extends Component {
  id = 4;
  state = {
    toDoList: [
      {
        id: 1,
        text: '러닝 뛰기',
      },
      {
        id: 2,
        text: '공부하기',
      },
      {
        id: 3,
        text: '독서하기',
      },
    ],
    search: '',
  };

  handleCreate = () => {};
  handleUpdate = (id, data) => {};
  handleRemove = (id) => {};
  render() {
    const { toDoList } = this.state;
    return (
      <div>
        <ToDoForm onCreate={this.handleCreate} />
        <ToDoList data={toDoList} onUpdate={this.handleUpdate} onRemove={this.handleRemove} />
      </div>
    );
  }
}

export default App;

ToDoForm 컴포넌트는 할일 목록을 입력받아 toDoList 배열에 추가할 props handleCreate를 전달한다.

ToDoList는 할일 목록을 출력해주는 컴포넌트로 props로 toDoList, handleUpdate, handleRemove 를 컴포넌트에 전달한다.


우선 ToDoList 컴포넌트를 작성하여 미리 작성된 배열 오브젝트들을 보여주도록 하겠습니다.

 

ToDoList.js

import React, { Component } from 'react';
import ToDoInfo from './ToDoInfo';

class ToDoList extends Component {
  state = {
    style: {
      border: '1px solid black',
      padding: '25px',
      margin: '15px',
    },
  };
  render() {
    const { data, onUpdate, onRemove } = this.props;

    return (
      <div>
        <ul>
          {data.map((data) => (
            <li style={this.state.style}>
              <ToDoInfo data={data} onUpdate={onUpdate} onRemove={onRemove} />
            </li>
          ))}
        </ul>
      </div>
    );
  }
}

export default ToDoList;

props로 전달받은 data를 가져와 map 메소드를 사용하여 li를 출력해주도록 합니다.

여기서 li에 ToDoInfo 컴포넌트를 해준 이유는 map으로 각각의 배열을 출력할 때 해당하는 정보를 ToDoInfo 컴포넌트로 전달하여 보여주기 위함입니다. 


ToDoInfo.js

import React, { Component } from 'react';

class ToDoInfo extends Component {
  render() {
    const { data, onUpdate, onRemove } = this.props;
    return (
      <div>
        <span>{data.text}</span>
      </div>
    );
  }
}

export default ToDoInfo;

여기까지 작성했다면 아래와 같이 결과가 출력될 것 입니다.

이제 여기서 각각의 ToDoInfo에 수정과 삭제를 위해 버튼을 하나 생성하고 거기에 맞는 핸들 메소드를 작성하도록 하겠습니다.

 

수정

import React, { Component } from 'react';

class ToDoInfo extends Component {
  state = {
    toggle: false,
    text  : '',
    style : {
      margin: '10px',
    },
  };

  handleChange = ( e ) => {
    this.setState({
      [e.target.name]: e.target.value,
    });
  };
  handleToggleChange = () => {
    const { toggle, text } = this.state;
    const { data, onUpdate } = this.props;
    // false -> true
    if (!toggle) {
      this.setState({
        text  : data.text,
        toggle: true,
      });
    } else {
      onUpdate(data.id, { text: text });
      this.setState({
        toggle: false,
      });
    }
    // ture -> false
  };

  handleRemove = () => {};

  render() {
    const { data } = this.props;
    const { toggle, text } = this.state;
    console.log(data.text + ' 렌더링 됐어유');
    return (
      <div>
        {toggle ? (
          <input
            style={this.state.style}
            onChange={this.handleChange}
            value={text}
            name="text"
          ></input>
        ) : (
          <span style={this.state.style}>{data.text}</span>
        )}
        <button onClick={this.handleToggleChange}>{toggle ? '적용' : '수정'}</button>
        <button onClick={this.handleRemove}>삭제</button>
      </div>
    );
  }
}

export default ToDoInfo;

toggle 을 만들어 false와 true일 때 handleToggleChange 메소드를 이용하여 각각에 맞는 이벤트들을 처리하도록 했습니다.

handleToggleChange() 메소드에서는 필요한게 두 가지 입니다.

toggle이 false -> true 일 때와 ture -> false일 때 입니다. 

false -> ture  :  수정 버튼을 눌렀을 경우 input 창 value에 기존에 입력되었던 data.text값을 가져온다

toggle false -> true

ture => false : 적용 버튼을 누른 경우로 업데이트를 위해 App.js 컴포넌트의 handleUpdate로 보낸다

// App.js 에서의 handleUpdate 부분
handleUpdate = (id, data) => {
    console.log(id);
    console.log(data);
  };

이렇게 바뀐 값들이 handleUpdate 로 넘오오게 된다.

App.js / handleUpdate 부분

  handleUpdate = (id, data) => {
    const { toDoList } = this.state;

    this.setState({
      toDoList: toDoList.map((toDoList) => {
        console.log(toDoList);
        if (toDoList.id === id) {
          console.log(toDoList.id + ' / ' + id);
          return {
            id,
            ...data,
          };
        }
        return toDoList;
      }),
    });
  };

map  메소드를 사용하여 id값이 서로 같은 경우 수정하려는 배열이 맞으므로 거기만 새로 받아온 data 를 반환해준다.

 

삭제

ToDoInfo.js / handleRemove 부분

handleRemove = () => {
  const { data, onRemove } = this.props;
  onRemove(data.id);
};

App.js / handleRemove 부분

  handleRemove = (id) => {
    const { toDoList } = this.state;

    this.setState({
      toDoList: toDoList.filter((data) => data.id !== id),
    });
  };

삭제는 비교적 간단합니다. id값을 가져와 filter 메소드로 받아온 id값과 틀린 것만 toDoList에 반환시키고 id값이 같을 경우 반환시키지 않으면 됩니다.

 

추가

ToDoForm.js

import React, { Component } from 'react';

class ToDoForm extends Component {
  state = {
    text: '',
  };
  handleChange = (e) => {
    this.setState({
      [e.target.name]: e.target.value,
    });
  };
  handleSubmit = (e) => {
    e.preventDefault();
    this.props.onCreate(this.state);
    this.setState({
      text: '',
    });
  };
  render() {
    const { text } = this.state;
    return (
      <div>
        <form onSubmit={this.handleSubmit}>
          <input value={text} name="text" placeholder="..입력" onChange={this.handleChange}></input>
          <button type="submit">추가</button>
        </form>
      </div>
    );
  }
}

export default ToDoForm;

input onChange이벤트로 입력받은 값을 state 에 저장하고 submit 이벤트 발생시 handleSubmit에서는 this.props.onCreate(this.state)로 현재의 state 값을 부모 컴포넌트로 값을 보낸다.

 

App.js / handleCreate 부분

  handleCreate = (data) => {
    console.log(data);
  };

리스트에 추가하려는 데이터가 console창에서 확인결과 잘 받아오고 있다.

이제 받아온 값을 기존의 toDoList 배열에 추가만 해주면 된다.

  handleCreate = (data) => {
    const { toDoList } = this.state;

    this.setState({
      toDoList: toDoList.concat({
        id: this.id++,
        ...data,
      }),
    });
  };

concat을 사용하여 기존에 있던 toDoList배열에 추가하면 끝입니다.

 

검색

검색은 간단히 indexOf을 사용하여 ToDoList에 해당하는 값만 전달하여 검색하는 방식으로 하면 간단하게 구현할 수 있습니다.

 

App.js / handleSearch 부분

  handleSearch = (e) => {
    this.setState({
      [e.target.name]: e.target.value,
    });
  };

App.js / render 부분

  render() {
    const { toDoList, search } = this.state;
    return (
      <div>
        <ToDoForm onCreate={this.handleCreate} />
        <input value={search} name="search" onChange={this.handleSearch} placeholder=" ..검색" />
        <ToDoList
          data={toDoList.filter((data) => data.text.indexOf(search) !== -1)}
          onUpdate={this.handleUpdate}
          onRemove={this.handleRemove}
        />
      </div>
    );
  }

input에 onChange 를 사용해서 state에 있는 search 값을 저장해주도록했습니다.

그리고 ToDoList 컴포넌트에 data props를 전달해 줄 때 filter 메소드를 사용하여 text에 검색하려는 단어가 있을 경우에만 배열을 반환하여 ToDoList 컴포넌트에 전달하도록 했습니다.