리액트 데이터필터링 구현하기

벨로퍼트님 9편보고 작성한 내용입니다

App.js

import React, { Component } from 'react';
import PhoneForm from './components/PhoneForm';
import PhoneInfoList from './components/PhoneInfoList';

class App extends Component {
  id = 2
  state = {
    information: [
      {
        id: 0,
        name: '김민준',
        phone: '010-0000-0000'
      },
      {
        id: 1,
        name: '홍길동',
        phone: '010-0000-0001'
      }
    ],
    keyword: ''
  }
  handleChange = (e) => {
    this.setState({
      keyword: e.target.value,
    });
  }
  handleCreate = (data) => {
    const { information } = this.state;
    this.setState({
      information: information.concat({ id: this.id++, ...data }) 
      //새로운 정보가 form에 입력되어 전송되면,
      // information에 이어 붙이는것임 id랑 들어온 data 모두
      // 즉, id : this.id++ 
      // name : 사용자가 입력한 name
      // phone : 사용자가 입력한 phone 이 state.information에 저장된다.
    })
  }
  handleRemove = (id) => {
    const { information } = this.state;
    this.setState({
      information: information.filter(info => info.id !== id) 
      //information을 id빼고 다시 만든거임. 덮어쓰기 한 듯(정답ㅋㅋㅋ)
    })
  }
  handleUpdate = (id, data) => { //수정을 누른 글의 id와 data를 매개변수로 받아옴
    //data는 information이구만
    const { information } = this.state;
    this.setState({
      information: information.map(
        info => id === info.id
          ? { ...info, ...data } // 새 객체를 만들어서 기존의 값과 전달받은 data 을 덮어씀
          : info // 기존의 값을 그대로 유지
      )
    })
  }
  render() {
    const { information, keyword } = this.state;
    const filteredList = information.filter( //새로운 배열을 만드는 filter함수
      info => info.name.indexOf(keyword) !== -1 //info.name은 information(상태) 중에 
      //id,name,phone이 있었는데 그 중 name 중에서 keyword가 없으면 indexOf함수는 -1을
      //리턴 하기 때문에 -1이 아닌것으로 information 배열을 다시 만들겠다. 라는 뜻이다
    );
    return (
      <div>
        <PhoneForm
          onCreate={this.handleCreate}
        />
        <p>
          <input 
            placeholder="검색 할 이름을 입력하세요.." 
            onChange={this.handleChange}
            value={keyword}
          />
        </p>
        <hr />
        <PhoneInfoList data={filteredList}
          onRemove={this.handleRemove} 
          onUpdate={this.handleUpdate}/>
      </div>
    );
  }
}

export default App;


출처 : https://dubaiyu.tistory.com/80

handleChange 함수를 보면, keyword 를 e.target.value로 setState하고 잇다.
e.target이란 특정 이벤트가 발생하는 태그를 가르킨다.

<input 
            placeholder="검색 할 이름을 입력하세요.." 
            onChange={this.handleChange}
            value={keyword}
          />

에서 input태그 안에 onChange로 {this.handleChange} 를 부르기 때문에
e.target이 input태그를 가르키게 된다.
따라서 e.target.value는 인풋태그에 입력되는 값을 의미한다.

출처 : https://hianna.tistory.com/379
indexOf(찾을 값) 만 해도 indexOf 함수를 쓸 수 있다.

PhoneForm.js (9편에선 안건드림)

import React, { Component } from 'react';

class PhoneForm extends Component {
  state = {
    name: '',
    phone: '' //state에 name속성이 있어야 state.name, state.phone으로 불러올 수 있음.
  }
  handleChange = (e) => {
    this.setState({
        [e.target.name]: e.target.value //여기서 name은 name속성을 말하는 것인데, 
        //배열로 감싼 이유는 여러개 를 e.target.value로 넣어주려고 그런듯
    })
  }
  handleSubmit = (e) => {
    // 페이지 리로딩 방지 (원래 form에서 submit이 발생하면 페이지 리로딩이되는데, 
    // 그러면 우리가 지니고있는 상태값을 다 잃어버리므로 방지시킴)
    e.preventDefault(); //원래 이벤트가 해야하는 작업 방지
    
    // 상태값을 onCreate 를 통하여 부모에게 전달 (props로 애초에 부모인 App.js에서 
    // onCreate함수가 정의되어있는데, 그 함수를 그냥 쓴거다. 자식에서 그러니까
    // 사실은 여기서 부모에게 전달해준다고 이해한다기 보다는 , 부모에서 PhoneForm에다가 
    // onCreate함수를 써놨기 때문에 상태값을 전달해줄 수밖에 없는 것이다.)
    this.props.onCreate(this.state);
    // 상태 초기화
    this.setState({
      name: '',
      phone: ''
    })
  }
  render() {
    return (
        // onSubmit을 안써주면 말짱도루묵ㅋㅋ
      <form onSubmit={this.handleSubmit}>
        <input
          placeholder="이름"
          value={this.state.name}
          onChange={this.handleChange}
          name="name" //name 속성 설정 필수
        />
       <input
          placeholder="전화번호"
          value={this.state.phone}
          onChange={this.handleChange}
          name="phone"
        />
        <button type="submit">등록</button>
      </form>
    );
  }
}

export default PhoneForm;

PhoneInfo.js

import React, { Component } from 'react';

class PhoneInfo extends Component {
  static defaultProps = {
    info: {
      name: '이름',
      phone: '010-0000-0000',
      id: 0
    },
  }
  state = {
    // 우리는 수정 버튼을 눌렀을 떄 editing 값을 true 로 설정해줄것입니다.
    // 이 값이 true 일 때에는, 기존에 텍스트 형태로 보여주던 값들을
    // input 형태로 보여주게 됩니다.
    editing: false,
    // input 의 값은 유동적이겠지요? input 값을 담기 위해서 각 필드를 위한 값도
    // 설정합니다
    name: '',
    phone: '',
  }

  handleRemove = () => {
    // 삭제 버튼이 클릭되면 onRemove 에 id 넣어서 호출
    const { info, onRemove } = this.props;
    onRemove(info.id);
  }

  // editing 값을 반전시키는 함수입니다
  // true -> false, false -> true
  handleToggleEdit = () => {
    const { editing } = this.state;
    this.setState({ editing: !editing }); //true를 false로 반전시키는 기능을 하나보다
    //editing이 true던 false던 반대로 해주는 역할
    //true가 되면 수정 창이 열리는듯
  }

  // input 에서 onChange 이벤트가 발생 될 때
  // 호출되는 함수입니다
  handleChange = (e) => {
    const { name, value } = e.target;
    this.setState({
      [name]: value
    });
  }
  shouldComponentUpdate(nextProps, nextState) {
    // 수정 상태가 아니고, info 값이 같다면 리렌더링 안함
    if (!this.state.editing  
        && !nextState.editing
        && nextProps.info === this.props.info) {
      return false;
    }
    // 나머지 경우엔 리렌더링함
    return true;
  }
  componentDidUpdate(prevProps, prevState) {
    //라이프생명주기 : 컴포넌트 업데이트가 끝나면 자동 실행
    //prevState는 내장함수여서 자동으로 이전 상태값을 불러와준다.!!!!

    // 여기서는, editing 값이 바뀔 때 처리 할 로직이 적혀있습니다.
    // 수정을 눌렀을땐, 기존의 값이 input에 나타나고,
    // 수정을 적용할땐, input 의 값들을 부모한테 전달해줍니다.

    const { info, onUpdate } = this.props;
    if(!prevState.editing && this.state.editing) {
      // editing 값이 false -> true 로 전환 될 때 
      //(수정 버튼을 눌러서 이전 정보가 input창이 열렸을 때)
      // info 의 값을 state 에 넣어준다
      this.setState({
        name: info.name,
        phone: info.phone
      })
    }

    if (prevState.editing && !this.state.editing) {
      // editing 값이 true -> false 로 전환 될 때 (수정 적용 버튼 눌렀을 때)
      onUpdate(info.id, {
        name: this.state.name,
        phone: this.state.phone
      });
    }
  }

  render() {
    console.log('render PhoneInfo ' + this.props.info.id);
    const style = {
      border: '1px solid black',
      padding: '8px',
      margin: '8px'
    };

    const { editing } = this.state;

    
    if (editing) { // 수정모드
      return (
        <div style={style}>
          <div>
            <input
              value={this.state.name}
              name="name"
              placeholder="이름"
              onChange={this.handleChange}
            />
          </div>
          <div>
            <input
              value={this.state.phone}
              name="phone"
              placeholder="전화번호"
              onChange={this.handleChange}
            />
          </div>
          <button onClick={this.handleToggleEdit}>적용</button>
          <button onClick={this.handleRemove}>삭제</button>
        </div>
      );
    }

    //일반모드
    const {
      name, phone
    } = this.props.info;
    
    return (
      <div style={style}>
        <div><b>{name}</b></div>
        <div>{phone}</div>
        <button onClick={this.handleToggleEdit}>수정</button>
        <button onClick={this.handleRemove}>삭제</button>
      </div>
    );
  }
}

export default PhoneInfo;

PhoneInfoList.js

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

class PhoneInfoList extends Component {
  static defaultProps = {
    data: [],
    onRemove: () => console.warn('onRemove not defined'),
    onUpdate: () => console.warn('onUpdate not defined'),
  }
  shouldComponentUpdate(nextProps, nextState) { //prevProps, prevState처럼 
    //내장되어있는 거겠지
    //render함수보다 먼저 호출되는 특수한 함수이며, 
    //해당 컴포넌트의 렌더링을 생략할 수 있는 기회를 제공한다
    //성능 최적화에 유용한 함수이다. (속성변경 라이프사이클API)
    return nextProps.data !== this.props.data; //값이 달라야지만 return 해주겟다...?
  }
  render() {
    console.log('render PhoneInfoList');
    const { data , onRemove , onUpdate} = this.props;
    const list = data.map( 
      info => (
      <PhoneInfo 
        key={info.id} 
        info={info}
        onRemove={onRemove} 
        onUpdate={onUpdate}/>) //이 info가 저 info인지 어케알지,,,,?? 
          //=> data를 하나씩 쪼갠걸 info로 불러가지구,,,,
      //data가 props로 받은걸 data로 하는데 그 데이터 덩어리를 하나씩 쪼갠게 info고,,,
        //그 info를 info로 넣겠다...?
      //onRemove는 삭제할 id값이 PhoneInfo 에서 넘어온다.
    );

    return (
      <div>
        {list}    
      </div>
    );
  }
}

export default PhoneInfoList;

출처

벨로퍼트님 9편

좋은 웹페이지 즐겨찾기