KIC/React

[React] 배열출력,전화번호부 미니프로젝트-데이터추가

octopengj 2020. 11. 10. 09:07

create-react-app my-telephone

 

src

  | 

  component - App.js 이동

 

 

부트스트랩 설치

yarn add bootstrap@3.4.1

 

src/index.js(상위컴포넌트에 부트스트랩라이브러리 등록)

 

Contact.js

import React, { Component } from 'react';
import ContactDetails from './ContactDetails';
//추가
import ContactInfo from './ContactInfo';
//추가(배열의 추가,삭제,수정)->update
import update from 'react-addons-update';
import ContactCreate from './ContactCreate';

class Contact extends Component {
    //1.데이터관리(추가,수정,삭제,조회)->생성자를 통해서 초기화
    constructor(props){
        super(props);
        this.state={
            keyword:'', /* 실시간으로 입력할 데이터를 저장할 속성명*/
            selectedKey:-1, /* 선택한 항목저장  */
            //(2)배열의 초기화
            contactData:[   //this.state.contactData[1]
                {name:'홍길동',phone:'010-123-9876'},
                {name:'테스트',phone:'010-666-9999'},
                {name:'임  시',phone:'010-123-2222'},
                {name:'임시2',phone:'010-123-3333'},
                {name:'테스트김',phone:'010-123-4444'}
            ]
        }
        //이벤트 연결구문=>onChange={this.handleChange.bind(this)}
        this.handleChange=this.handleChange.bind(this)
        this.handleClick=this.handleClick.bind(this)
        //추가
        this.handleCreate=this.handleCreate.bind(this)
        this.handleEdit=this.handleEdit.bind(this)
        this.handleRemove=this.handleRemove.bind(this)
    }
    ///배열의 추가,수정,삭제 함수선언//
    handleCreate(contact){
      this.setState({ 
        //형식)배열의 키명:update(1.집어넣을 데이터대상자,2.$push이용[]로 묶어줄것)
        contactData:update(this.state.contactData,{$push:[contact]})
      })
    }
    handleEdit(name,phone){ //데이터 수정(,addr,sung,position,,,)
        this.setState({
         contactData:update(this.state.contactData,{
            //2.수정할값에 대한 인덱스번호:{키명:{$set:수정할값},,}
            [this.state.selectedKey]:{
              name:{$set:name}, //형식)name(키명):{$set:저장할값}
              phone:{$set:phone}//,addr:{$set:addr(서울시 강남구)}
            }
         })
        })
    }
    handleRemove(){//데이터 삭제
      //추가->선택하지 않으면 삭제되지않게 설정
      if(this.state.selectedKey < 0){
          return; //밑의 삭제하는 구문 실행하지 않고 종료
      }
      this.setState({
          contactData:update(this.state.contactData,{
            //$splice키를 이용해서 선택된 키로부터 몇개를 삭제?
            //삭제할 대상자도 배열로 묶어준다.(1개라도) 
            //selectedKey(현재 행)을 기준해서 1개삭제
            $splice:[[this.state.selectedKey,1]]
          }),
          selectedKey:-1 //다시 삭제시킬 대상자를 선택하기위해서 초기화
      })
    }

    //이벤트처리해주는 함수작성
    handleChange(e){ //const handleChange=(e)=>{}
      //jQuery->$(this)->event.target(이벤트가 발생된 객체)
      this.setState({keyword:e.target.value})
    }
    //key값을 매개변수로 처리해주는 함수
    handleClick(key){
      this.setState({selectedKey:key})
      console.log(key,'is selected!')
    }
    //render 함수전에 호출-> DB에 저장된 데이터를 가져와서 출력
    componentWillMount(){ //localStorage.contactData(키명)
      const contactData=localStorage.contactData
      if(contactData){ //불러올 데이터가 있다면
        this.setState({
          contactData:JSON.parse(contactData) //문자열->객체변환(저장)
        })
      }
    }
    //화면에 데이터를 수정할떄마다 호출
    componentDidUpdate(prevProps,prevState){
      //이미 저장된 과거의 데이터와 현재 데이터가 다르면 서로 비교
      if(JSON.stringify(prevState.contactData)!=JSON.stringify(this.state.contactData)){
        localStorage.contactData=JSON.stringify(this.state.contactData)
      }
      console.log('componentDidUpdate()호출됨!')
    }
    render() {
        //화살표함수를 이용=>하나씩 분리해서 인덱스별로 출력
        const mapToComponents=(data)=>{
            //---추가(검색한 데이터를 찾아서 필터)->ContactInfo전달
            data=data.sort()//1.정렬
            data=data.filter((contact)=>{
                //검색할 데이터가 없다면 indexOf()->-1리턴(못찾음)
               return contact.name.indexOf(this.state.keyword) > -1;
            }) //2.정렬된 데이터중에서 찾은데이터만
            //---const 함수명=()=>{}
            //console.log('검색한 data=>',data)//배열전체 or 일부
            return data.map((contact,i)=>{
            //contact(배열의 각각요소를 의미),i=>요소의 인덱스번호
              //console.log('contact->',contact,'i->',i)
              //부->자식(props)->문자,숫자,함수,객체도 전달
              //key->내부적인 contact를 구분하는 인자->배열의 인덱스번호
              //각 객체을 구분해주는 인덱스i를 여기에서 처리
              /*
                 const test=()=> //function test()
                    this.handleClick(i)  =>this.props.onClick
                 
              */
              return(<ContactInfo contact={contact} key={i}
                      onClick={()=>this.handleClick(i)}/>)
            })
        }
        return (
        <div>
            <div class="form-group">
                <label for="Name">검색</label>
                <input name="keyword" placeholder="Search" 
                       class="form-control" value={this.state.keyword}
                       onChange={this.handleChange}/>
            </div>
            <div>
                {mapToComponents(this.state.contactData)}
                <ContactDetails  
                     isSelected={this.state.selectedKey!= -1} 
                contact={this.state.contactData[this.state.selectedKey]}
                     onRemove={this.handleRemove}
                     onEdit={this.handleEdit}/>
                <ContactCreate onCreate={this.handleCreate}/>
                {/*
                <div>홍길동 010-1234-0987</div>
                <div>테스트 010-1234-8888</div>
                <div>임  시 010-444-0987</div>
                <div>임시2 010-1234-1111</div>
                <div>테스트김 010-7777-0987</div>*/}  
            </div>
        </div>
        );
    }
}

export default Contact;

 

App.js

import React, { Component } from 'react';
//추가
import Contact from './Contact';

class App extends Component {
  render() {
    return (
      <div>
        <h1 className="text-center">전화번호부</h1>
        {/* (1)
        <div>홍길동 010-1234-0987</div>
        <div>테스트 010-1234-8888</div>
        <div>임  시 010-444-0987</div>
        <div>임시2 010-1234-1111</div>
        <div>테스트김 010-7777-0987</div> */}
        <Contact />
      </div>
    );
  }
}

export default App;

 

 

ContactCreate.js

import React, { Component } from 'react';

export default class ContactCreate extends Component {
    //생성자->create버튼을 누르기전까지는 임시로 입력한 값을 저장
    constructor(props){
        super(props)
        this.state={
            name:'',
            phone:''//,addr:'',sung:'',,,, 입력한 갯수 설정
        }
        this.handleChange=this.handleChange.bind(this)
        this.handleKeyPress=this.handleKeyPress.bind(this)
        this.handleClick=this.handleClick.bind(this)
    }
    
    //enter칠때 추가버튼을 클릭
    handleKeyPress(e){//마우스좌표(x,y),특정한키(charCode),이벤트발생
      if(e.charCode==13){//enter를 눌렀다면
        this.handleClick()//추가버튼
      }
    }
    //추가
    handleChange(e){//event객체->값을 입력,마우스좌표값(x,y)
      let nextState={} //빈객체 생성 {name:'홍길동',phone:'02-123-1111'}
       //nextState[name]='홍길동' e.target->입력하고 있는 input
       nextState[e.target.name]=e.target.value
       this.setState(nextState)
    }
    //추가2
    handleClick(){ //부모함수를 호출(저장할값->초기화)
      const contact={
          name:this.state.name,
          phone:this.state.phone//,addr:this.state.addr,,,
      }
      //부모함수를 호출해서 전달
      this.props.onCreate(contact)//handleCreate(저장할값)
      //저장하고나서 초기화작업->''
      this.setState({
          name:'',
          phone:''
      })
    }
    render() {
        return (
            /* before
            <div>
              <p>
               <input type="text" name="name" placeholder="name"
                      value={this.state.name} 
                      onChange={this.handleChange} />

               <input type="text" name="phone" placeholder="phone" 
                      value={this.state.phone}
                      onChange={this.handleChange} />    
              </p> 
              <button onClick={this.handleClick}>Create</button>
            </div>*/
            <div class="container">
              <div class="col-sm-6 col-md-offset-3">
              <div class="form-group">
                <label for="name">이름</label>
               <input type="text" class="form-control" id="name"
                      name="name" placeholder="이름을 입력"
                      value={this.state.name} 
                      onChange={this.handleChange} />
              </div>
              <div class="form-group">
                <label for="name">전번</label> 
               <input type="text" class="form-control" id="phone"
                      name="phone" placeholder="전화번호를 입력" 
                      value={this.state.phone}
                      onChange={this.handleChange}
                      onKeyPress={this.handleKeyPress} />    
              </div>
              <div class="form-group text-center">
              <button onClick={this.handleClick} 
                      class="btn btn-success">추가</button>
              </div>
                {/* </form> */}
            </div>
        </div>
        )
  }
}

 

ContactDetail.js

import React, { Component } from 'react';

class ContactDetails extends Component {
    //추가(편집상태확인)
    constructor(props){
        super(props)
        this.state={
            isEdit:false, //편집유무 체크속성
            name:'',
            phone:''
        }
        this.handleToggle=this.handleToggle.bind(this)
        this.handleChange=this.handleChange.bind(this)
        //추가
        this.handleEdit=this.handleEdit.bind(this)
    }
    //부모함수호출할 함수작성
    handleEdit(){
       this.props.onEdit(this.state.name,this.state.phone)
    }
    handleToggle(){ //화면전환(true(보여주면서 편집<->false(보여주기만))
        //부모의 handleEdit를 호출할 수있도록 추가코딩
        //편집상태라면 
        if(!this.state.isEdit){//수정
            this.setState({
               name:this.props.contact.name,
               phone:this.props.contact.phone //
               //,addr:this.props.contact.addr
               //,age:this.props.contact.age
            })
        }else{//수정할거 다하고나서 확인버튼(false)
          this.handleEdit()//자기 클래스내부에서 다른 함수호출시
        }                  //this.호출할함수명(~)
        this.setState({
           isEdit:!this.state.isEdit
        })
        //위의 setState()가 실행이 되기전에 먼저 console문장을 수행
        console.log(!this.state.isEdit)
    }
    //추가(handleChange)ContactCreate.js에서 복사해올것.
    handleChange(e){//event객체->값을 입력,마우스좌표값(x,y)
        let nextState={} //빈객체 생성 {name:'홍길동',phone:'02-123-1111'}
         //nextState[name]='홍길동' e.target->입력하고 있는 input
         nextState[e.target.name]=e.target.value
         this.setState(nextState)
    }
    //--------------------
    render() {
        //추가 선택한 항목이 있으면 항목출력,-1 (선택X 하지않음을 표시)
        const details=(<div>
                        <p>{this.props.contact.name}</p>
                        <p>{this.props.contact.phone}</p>
                      </div>)
        //추가
        //const view=this.state.isEdit?(<div></div>):details;
        const edit=(
        <div>
          <div class="col-sm-6 col-md-offset-3">
            <div class="form-group">
              <label for="name">이름</label>
             <input type="text" class="form-control" id="name"
                    name="name" placeholder="이름을 입력"
                    value={this.state.name} 
                    onChange={this.handleChange} />
            </div>
            <div class="form-group">
              <label for="name">전번</label> 
             <input type="text" class="form-control" id="phone"
                    name="phone" placeholder="전화번호를 입력" 
                    value={this.state.phone}
                    onChange={this.handleChange} />    
            </div>
          </div>
        </div>
        )
        //선택한 상태(글상세보기에서 수정버튼을 눌렀는지유무)
        const view=this.state.isEdit?edit:details;
        const blank=(<div>Not Selected!!!</div>)
        //Contact.js에서 isSelected={선택한 항목의 index}
        return (
            <div className="text-center text-success">
                {this.props.isSelected?view:blank}
                <p>
                <button onClick={this.handleToggle} 
                        class="btn btn-info">
                    {this.state.isEdit?'확인':'수정'}
                </button>
                <button onClick={this.props.onRemove} class="btn btn-danger">삭제</button>
                </p>
            </div>
        );
    }
}
//처음에는 선택을 할 수가 없기때문에 에러발생->화면에 출력X
/*
String pageNum=request.getParameter("pageNum");
  if(pageNum==null)  pageNum="1"->1
*/
ContactDetails.defaultProps={
    contact:{
        name:'',
        phone:''//,addr:'',sung:'',,,
    },
    //함수의 경우 초기값을 설정보다는 에러메세지를 출력
    onRemove:()=>{console.error('onRemove not defined!')}
}
export default ContactDetails;

 

ContactInfo.js

import React, { Component } from 'react';

class ContactInfo extends Component {
    //자식함수<- this.props.onClick(key)
    render() {
        /*
          this.props.contact(전달된 배열요소의 값)->키명분리
        return (
            <div>
              {this.props.contact.name} {this.props.contact.phone}
            </div>
        );*/
        return (
            <div className="container">
            <table className="table table-bordered">
              <tr className="success">
              <td onClick={this.props.onClick}>{this.props.contact.name}</td>
              </tr>
            </table>
            </div>)
    }
}

export default ContactInfo;

 

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
//추가
import App from './components/App';
//추가
import 'bootstrap/dist/css/bootstrap.css';
//-----------------------------------------
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();