반응형

1. App.js

import React from 'react';
import Contact from './Contact';
import './App.css';

function App() {
  return (
    <Contact />
  );
}

export default App;

2. Contact.js

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

class Contact extends Component {
    constructor(props) {
        super(props);
        this.state = {
            selectedKey: -1,
            keyword: '',
            contactData: [{
                name: 'Abet',
                phone: '010-000-0001'
            }, {
                name: 'Betty',
                phone: '010-000-0002'
            }, {
                name: 'Charlie',
                phone: '010-000-0003'
            }, {
                name: 'David',
                phone: '010-000-0004'
            }]
        }

    }

    render() {

        const mapToComponents = (data) => {
            return data.map((contact, i) => {
                return (<ContactInfo contact={contact} key={i} />);
            })
        }
        return (
            <div>
                <h1>Contacts</h1>
                <div>{mapToComponents(this.state.contactData)}</div>
            </div>
        );
    }
}

export default Contact;

 

3. ContactInfo.js

import React, { Component } from 'react';

class ContactInfo extends Component {
    render() {
        return (
            <div>{this.props.contact.name}{this.props.contact.phone}</div>
        );
    }
}

export default ContactInfo;
반응형
반응형

LocalStorage에 저장하여 update되어도 데이터가 초기상태로 돌아가지 않도록 하는 기능을 구현하겠습니다. 

componentWillMount API와 componentDidUpdate API만 구현해주면 됩니다.

 

Contact.js

import React, { Component } from 'react';
import ContactInfo from './ContactInfo';
import ContactDetails from './ContactDetails';
import ContactCreate from './ContactCreate';

class Contact extends Component {
    constructor(props) {
        super(props);
        this.state = {
            selectedKey: -1,
            keyword: '',
            contactData: [{
                name: 'Abet',
                phone: '010-000-0001'
            }, {
                name: 'Betty',
                phone: '010-000-0002'
            }, {
                name: 'Charlie',
                phone: '010-000-0003'
            }, {
                name: 'David',
                phone: '010-000-0004'
            }]
        }
        this.handleChange = this.handleChange.bind(this);
        this.handleClick = this.handleClick.bind(this);

        this.handleCreate = this.handleCreate.bind(this);
        this.handleRemove = this.handleRemove.bind(this);
        this.handleEdit = this.handleEdit.bind(this);

    }

	// 로컬 스토리지 데이터 가져오기 부분
    componentWillMount() {   // 컴포넌트가 마운트 되기 전에 실행되는 API
        const contactData = localStorage.contactData;

        if (contactData) {    // 로컬 스토리지에 값이 있으면 JSON 변환하여 불러옴 
            this.setState({
                contactData: JSON.parse(contactData)
            })
        }
    }

    componentDidUpdate(prevProps, prevState) {   // 컴포넌트가 업데이트될 때마다 실행되는 API
        if (JSON.stringify(prevState.contactData) != JSON.stringify(this.state.contactData)) {  // 이전 값과 지금 값이 다르면
            localStorage.contactData = JSON.stringify(this.state.contactData);                  // 지금 값을 입력해 줌
        }
    }

    handleChange(e) {
        this.setState({
            keyword: e.target.value
        })
    }

    handleClick(key) {
        this.setState({
            selectedKey: key
        })
    }

    handleCreate(contact) {
        var newContact = Array.from(this.state.contactData);
        newContact.push(contact);
        this.setState({
            contactData: newContact
        });
    }

    handleRemove() {
        if (this.state.selectedKey < 0) {
            return;
        }
        var newContact = Array.from(this.state.contactData);
        newContact.splice(this.state.selectedKey, 1);
        this.setState({
            contactData: newContact,
            selectedKey: -1
        });

    }

    handleEdit(_name, _phone) {
        var newContact = Array.from(this.state.contactData);
        newContact[this.state.selectedKey] = { name: _name, phone: _phone };
        this.setState({
            contactData: newContact
        });
    }

    render() {

        const mapToComponents = (data) => {
            data = data.filter((contact) => {
                return contact.name.toLowerCase().indexOf(this.state.keyword.toLowerCase()) > -1;
            })
            return data.map((contact, i) => {
                return (<ContactInfo
                    contact={contact}
                    key={i}
                    onClick={() => this.handleClick(i)}
                />);
            })
        }
        return (
            <div>
                <h1>Contacts</h1>
                <input
                    name="keyword"
                    placeholder="Search"
                    value={this.state.keyword}
                    onChange={this.handleChange}
                />
                <div>{mapToComponents(this.state.contactData)}</div>
                <ContactDetails
                    isSelected={this.state.selectedKey != -1}
                    contact={this.state.contactData[this.state.selectedKey]}
                    onRemove={this.handleRemove}
                    onEdit={this.handleEdit}
                />
                <ContactCreate
                    onCreate={this.handleCreate}
                />
            </div>
        );
    }
}

export default Contact;
반응형
반응형

Update기능은 좀 어렵습니다. 구현해야 할 내용으로는 ContactDetails 컴포넌트에서

 

  1. Edit 버튼을 만듭니다. 이 버튼은 Edit모드와 비 Edit모드사이를 토글할 수 있도록 합니다.

  2. 토글 함수를 선언해줍니다. isEdit 값이 false이면 Edit 폼에 name과 phone이 변경 가능하도록 데이터를 보여주며, true이면(수정 완료버튼 클릭) name과 phone 값을 상위요소인 Contact 컴포넌트로 전달해줍니다. 전달함수는 별도로 3번에서 만듭니다.

  3. handleEdit 함수를 선언합니다. 이 함수에서 isEdit가 true일 때 name과 phone 을 Contact 컴포넌트로 전달해줍니다.

  4. Edit 폼을 만들어줍니다. 그리고 isEdit 값에 따라 표시되는 상황이 변경되도록(수정폼/뷰폼) 수정합니다.

 

그리고 Contact 컴포넌트에서

  5. handleEdit 함수를 선언합니다.  ContactDetails 컴포넌트의 handleEdit함수에서 전달된 name과 phone 값을 이용하여 state를 변경합니다. 동일한 함수 이름이지만 컴포넌트 내부에서만 쓰이므로 역할이 다릅니다. 다른 이름으로 해도 상관없습니다.

  6. <ContactDetails> 컴포넌트 내부에서 onEdit 이벤트를 handleEdit 함수와 연결해줍니다.

 

 

ContactDetails.js

import React, { Component } from 'react';

class ContactDetails extends Component {
    constructor(props) {
        super(props);

        this.state = {
            isEdit: false,  // 토글시킬 변수를 선언하고 초기값은 edit하지 않도록 false선언
            name: '',
            phone: '',
        };

        this.handleToggle = this.handleToggle.bind(this);   // isEdit를 토글시킬 함수 바인딩 선언
        this.handleChange = this.handleChange.bind(this);   // Edit 폼에서 입력 가능하도록 바인딩 선언
        this.handleEdit = this.handleEdit.bind(this);       // 진짜 edit하는 함수 바인딩 선언
    }

    handleToggle() {
        if (!this.state.isEdit) {   // Edit 폼에 수정할(선택할) 데이터가 보이도록 함.
            this.setState({
                name: this.props.contact.name,  // state로 선언된 name에 선택하면서 넘겨온 props의 name을 줌
                phone: this.props.contact.phone // state로 선언된 phone 선택하면서 넘겨온 props의 phone을 줌
            });
        } else {
            this.handleEdit();
        }
        this.setState({
            isEdit: !this.state.isEdit  // isEdit 변수를 토글함
        });
        console.log(this.state.isEdit);
    }

    handleChange(e) {           // edit폼에서 입력(변경) 가능하도록 함수 선언
        let nextState = {};
        nextState[e.target.name] = e.target.value;
        this.setState(nextState);
    }

    handleEdit() {      // state를 props의 onEdit 함수로 전달만 함.
        this.props.onEdit(this.state.name, this.state.phone);
    }

    render() {


        const details = (
            <div>
                <p>{this.props.contact.name}</p>
                <p>{this.props.contact.phone}</p>
            </div>
        );

        const edit = (   // edit 가능하도록 하는 폼 선언
            <div>
                <p>
                    <input
                        type="text"
                        name="name"
                        placeholder="name"
                        value={this.state.name}
                        onChange={this.handleChange}
                    />
                </p>
                <p>
                    <input
                        type="text"
                        name="phone"
                        placeholder="phone"
                        value={this.state.phone}
                        onChange={this.handleChange}
                    />
                </p>
            </div>
        )
        const view = this.state.isEdit ? edit : details;   // isEdit가 true이면 edit 화면, false이면 details를 보여줄 변수(view)선언

        const blank = (<div>Not Selected</div>)
        return (
            <div>
                <h2>Details</h2>
                {this.props.isSelected ? view : blank}  {/*선택된 상태이면 view를, 아니면 blank를 보여주도록 detail -> view로 수정*/}
                <p>
                    <button onClick={this.handleToggle}> {/* Edit 버튼 추가, 토글 함수에 연결 */}
                        {this.state.isEdit ? 'OK' : 'Edit'}  {/* isEdit가 true이면(수정 완료) OK가 표시되도록 */}
                    </button>
                    <button onClick={this.props.onRemove}>Remove</button> {/* 삭제 버튼 추가 */}
                </p>
            </div>
        );
    }
}

ContactDetails.defaultProps = {
    contact: {
        name: '',
        phone: ''
    },
    onRemove: () => { console.error('onRemove not defined'); },
    onEdit: () => { console.error('onEdit not defined'); }
}

export default ContactDetails;

 

 

Contact.js

import React, { Component } from 'react';
import ContactInfo from './ContactInfo';
import ContactDetails from './ContactDetails';
import ContactCreate from './ContactCreate';

class Contact extends Component {
    constructor(props) {
        super(props);
        this.state = {
            selectedKey: -1,
            keyword: '',
            contactData: [{
                name: 'Abet',
                phone: '010-000-0001'
            }, {
                name: 'Betty',
                phone: '010-000-0002'
            }, {
                name: 'Charlie',
                phone: '010-000-0003'
            }, {
                name: 'David',
                phone: '010-000-0004'
            }]
        }
        this.handleChange = this.handleChange.bind(this);
        this.handleClick = this.handleClick.bind(this);

        this.handleCreate = this.handleCreate.bind(this);
        this.handleRemove = this.handleRemove.bind(this);
        this.handleEdit = this.handleEdit.bind(this);

    }

    handleChange(e) {
        this.setState({
            keyword: e.target.value
        })
    }

    handleClick(key) {
        this.setState({
            selectedKey: key
        })
    }

    handleCreate(contact) {
        var newContact = Array.from(this.state.contactData);
        newContact.push(contact);
        this.setState({
            contactData: newContact
        });
    }

    handleRemove() {
        if (this.state.selectedKey < 0) {
            return;
        }
        var newContact = Array.from(this.state.contactData);
        newContact.splice(this.state.selectedKey, 1);
        this.setState({
            contactData: newContact,
            selectedKey: -1
        });

    }

    handleEdit(_name, _phone) {  // onEdit 이벤트 발생시 실행할 함수 선언
        var newContact = Array.from(this.state.contactData);    // 배열 복제
        newContact[this.state.selectedKey] = { name: _name, phone: _phone };    // 선택된 키의 요소에 대해 name과 phone에 전달된 값을 넣어준다.
        this.setState({
            contactData: newContact
        });
    }

    render() {

        const mapToComponents = (data) => {
            data = data.filter((contact) => {
                return contact.name.toLowerCase().indexOf(this.state.keyword.toLowerCase()) > -1;
            })
            return data.map((contact, i) => {
                return (<ContactInfo
                    contact={contact}
                    key={i}
                    onClick={() => this.handleClick(i)}
                />);
            })
        }
        return (
            <div>
                <h1>Contacts</h1>
                <input
                    name="keyword"
                    placeholder="Search"
                    value={this.state.keyword}
                    onChange={this.handleChange}
                />
                <div>{mapToComponents(this.state.contactData)}</div>
                <ContactDetails
                    isSelected={this.state.selectedKey != -1}
                    contact={this.state.contactData[this.state.selectedKey]}
                    onRemove={this.handleRemove}
                    onEdit={this.handleEdit}    // onEdit 이벤트를 handleEdit 함수로 연결
                />
                <ContactCreate
                    onCreate={this.handleCreate}
                />
            </div>
        );
    }
}

export default Contact;

 

반응형
반응형

삭제는

  1. ContactDetails컴포넌트에서 삭제버튼을 추가하고 onRemove(props) 이벤트를 추가하는 것과

  2. Contact 컴포넌트에서 handleRemove 함수를 선언하고 onRemove이벤트에 연결하는 것

이 필요합니다.

 

ContactDetails.js

import React, { Component } from 'react';

class ContactDetails extends Component {
    render() {
        const details = (
            <div>
                <p>{this.props.contact.name}</p>
                <p>{this.props.contact.phone}</p>
            </div>
        );
        const blank = (<div>Not Selected</div>)
        return (
            <div>
                <h2>Details</h2>
                {this.props.isSelected ? details : blank}
                <button onClick={this.props.onRemove}>Remove</button> {/* 삭제 버튼 추가, onRemove이벤트 추가 */}
            </div>
        );
    }
}

ContactDetails.defaultProps = {
    contact: {
        name: '',
        phone: ''
    },
    onRemove: () => { console.error('onRemove not defined'); }
}

export default ContactDetails;

 

 

Contact.js

import React, { Component } from 'react';
import ContactInfo from './ContactInfo';
import ContactDetails from './ContactDetails';
import ContactCreate from './ContactCreate';

class Contact extends Component {
    constructor(props) {
        super(props);
        this.state = {
            selectedKey: -1,
            keyword: '',
            contactData: [{
                name: 'Abet',
                phone: '010-000-0001'
            }, {
                name: 'Betty',
                phone: '010-000-0002'
            }, {
                name: 'Charlie',
                phone: '010-000-0003'
            }, {
                name: 'David',
                phone: '010-000-0004'
            }]
        }
        this.handleChange = this.handleChange.bind(this);
        this.handleClick = this.handleClick.bind(this);

        this.handleCreate = this.handleCreate.bind(this);
        this.handleRemove = this.handleRemove.bind(this);

    }

    handleChange(e) {
        this.setState({
            keyword: e.target.value
        })
    }

    handleClick(key) {
        this.setState({
            selectedKey: key
        })
    }

    handleCreate(contact) {
        var newContact = Array.from(this.state.contactData);
        newContact.push(contact);
        this.setState({
            contactData: newContact
        });
    }

    handleRemove() {    // onRemove이벤트와 연결할 함수 선언
        if (this.state.selectedKey < 0) {   //  아무 키가 선택되지 않았을 때에는 그냥 return
            return;
        }
        var newContact = Array.from(this.state.contactData);    // contactData 복제
        newContact.splice(this.state.selectedKey, 1);           // 복제 배열에서 selectedKey로부터 1개의 요소 삭제
        this.setState({
            contactData: newContact,               // 수정된 복제배열을 새로운 state로 넘김
            selectedKey: -1                        // selectedKey를 다시 -1로 원복시킴
        });

    }

    render() {

        const mapToComponents = (data) => {
            data = data.filter((contact) => {
                return contact.name.toLowerCase().indexOf(this.state.keyword.toLowerCase()) > -1;
            })
            return data.map((contact, i) => {
                return (<ContactInfo
                    contact={contact}
                    key={i}
                    onClick={() => this.handleClick(i)}
                />);
            })
        }
        return (
            <div>
                <h1>Contacts</h1>
                <input
                    name="keyword"
                    placeholder="Search"
                    value={this.state.keyword}
                    onChange={this.handleChange}
                />
                <div>{mapToComponents(this.state.contactData)}</div>
                <ContactDetails
                    isSelected={this.state.selectedKey != -1}
                    contact={this.state.contactData[this.state.selectedKey]}
                    onRemove={this.handleRemove}    {/* onRemove이벤트에 함수 연결*/}
                />
                <ContactCreate
                    onCreate={this.handleCreate}
                />
            </div>
        );
    }
}

export default Contact;

 

반응형
반응형

Create 기능구현은 두 부분으로 나누어 생각할 수 있습니다.
  1. ContactCreate 컴포넌트(신규)에서 신규 데이터 생성 기능, 과
  2. Contact 컴포넌트에서 전달받은 data를 기존 state(배열)에 추가하는 기능

입니다.

 

ContactCreate.js

import React from 'react';

export default class ContactCreate extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            name: '',
            phone: ''
        };
        this.handleChange = this.handleChange.bind(this);
        this.handleClick = this.handleClick.bind(this);
    }

    handleChange(e) {   // input 창에 입력 가능하도록 만듬
        let nextState = {};
        nextState[e.target.name] = e.target.value;	// name에는 name입력값, phone에는 phone 입력값을 각각 저장
        this.setState(nextState)
    }

    handleClick() {
        const contact = {   // 한번 만들어지면 수정할 일이 없으므로  const로 선언
            name: this.state.name,
            phone: this.state.phone
        };

        this.props.onCreate(contact);

        this.setState({
            name: '',
            phone: ''
        });
    }

    render() {
        return (
            <div>
                <h2>Create Contact</h2>
                <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>
        )
    }
}

 


Contact.js

import React, { Component } from 'react';
import ContactInfo from './ContactInfo';
import ContactDetails from './ContactDetails';
import ContactCreate from './ContactCreate';


export default class Contact extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            selectedKey: -1,
            keyword: '',
            contactData: [{
                name: 'Abet',
                phone: '010-000-001'
            }, {
                name: 'Betty',
                phone: '010-000-002'
            }, {
                name: 'Charlie',
                phone: '010-000-003'
            }, {
                name: 'David',
                phone: '010-000-004'
            }]
        }
        this.handleChange = this.handleChange.bind(this);
        this.handleClick = this.handleClick.bind(this);

        this.handleCreate = this.handleCreate.bind(this);        
    }

    handleChange(e) {
        this.setState({
            keyword: e.target.value
        })
    }

    handleClick(key) {
        this.setState({
            selectedKey: key
        });
        console.log(key, 'is selected');
    }

    
    handleCreate(contact) {
        var newContact = Array.from(this.state.contactData);
        newContact.push(contact);
        this.setState({
            contactData: newContact
        });
    }

  
    render() {
        const mapToComponents = (data) => {     // 배열형 data 받아들임
            data.sort();    // 오름차순정렬
            data = data.filter((_contact) => {
                return _contact.name.toLowerCase().indexOf(this.state.keyword.toLowerCase()) > -1;   // contact 배열에서 keyword를 포함하는(indexOf) 요소를 찾아서(-1 보다 큰..) 반환 (모두 소문자로 변환하여 비교)
            })
            return data.map((_contact, i) => {   // 배열의 1개 원소 _contact 와 인덱스 i를 넘겨줌
                return (<ContactInfo
                    contact={_contact}
                    key={i}
                    onClick={() => this.handleClick(i)} // 화살표 함수로 함수를 호출해야 작동함
                />);
            });
        };

        return (
            <div>
                <h1>Contacts</h1>
                <input
                    name="keyword"
                    placeholder="Search"
                    value={this.state.keyword}
                    onChange={this.handleChange}
                />
                <div>{mapToComponents(this.state.contactData)}</div>
                <ContactDetails
                    isSelected={this.state.selectedKey != -1} // selectedKey가 -1이 아니면 참을 전달 
                    contact={this.state.contactData[this.state.selectedKey]}
                />
                <ContactCreate
                    onCreate={this.handleCreate}
                />
            </div>
        );
    }
}

 

반응형
반응형

1. ContactInfo 컴포넌트에서 phone 번호를 삭제하고, Click 이벤트를 연결합니다.

ContactInfo.js

import React, { Component } from 'react';

export default class ContactInfo extends React.Component {
    render() {
        return (
            <div onClick={this.props.onClick}>{this.props.contact.name}</div>   // onClick 을 Props로 받음
        );
    }
}

2. Contact 컴포넌트에서 onClick 이벤트에 해당하는 연결함수(handleClick)를 선언하고, <ContactInfo> 컴포넌트 내부에서 연결해줍니다. handleClick(i) 함수에서는 selectedKey 변수에 몇번째 요소인지(i)를 나타내는 key를 할당합니다. 그리고 기존 화면 요소에 추가로 ContactDetails 컴포넌트를 작성하여 화면에 렌더링합니다. 

ContactDetails 컴포넌트의 내부 props로는 isSelected와 contact를 선언하여 사용할 준비를 합니다.

Contact.js

import React, { Component } from 'react';
import ContactInfo from './ContactInfo';
import ContactDetails from './contactDetails';

export default class Contact extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            selectedKey: -1,
            keyword: '',
            contactData: [{
                name: 'Abet',
                phone: '010-000-001'
            }, {
                name: 'Betty',
                phone: '010-000-002'
            }, {
                name: 'Charlie',
                phone: '010-000-003'
            }, {
                name: 'David',
                phone: '010-000-004'
            }]
        }
        this.handleChange = this.handleChange.bind(this);
        this.handleClick = this.handleClick.bind(this);
    }

    handleChange(e) {
        this.setState({
            keyword: e.target.value
        })
    }

    handleClick(key) {
        this.setState({
            selectedKey: key
        });
        console.log(key, 'is selected');
    }

    render() {
        const mapToComponents = (data) => {     // 배열형 data 받아들임
            data.sort();    // 오름차순정렬
            data = data.filter((_contact) => {
                return _contact.name.toLowerCase().indexOf(this.state.keyword.toLowerCase()) > -1;   // contact 배열에서 keyword를 포함하는(indexOf) 요소를 찾아서(-1 보다 큰..) 반환 (모두 소문자로 변환하여 비교)
            })
            return data.map((_contact, i) => {   // 배열의 1개 원소 _contact 와 인덱스 i를 넘겨줌
                return (<ContactInfo
                    contact={_contact}
                    key={i}
                    onClick={() => this.handleClick(i)} // 화살표 함수로 함수를 호출해야 작동함
                />);
            });
        };

        return (
            <div>
                <h1>Contacts</h1>
                <input
                    name="keyword"
                    placeholder="Search"
                    value={this.state.keyword}
                    onChange={this.handleChange}
                />
                <div>{mapToComponents(this.state.contactData)}</div>
                <ContactDetails
                    isSelected={this.state.selectedKey != -1} // selectedKey가 -1이 아니면 참을 전달 
                    contact={this.state.contactData[this.state.selectedKey]}
                />
            </div>
        );
    }
}

 

3. 화면에 추가할 ContactDetails를 작성합니다. 특정인이 선택되었을 때 표시할 details 변수와 아무도 선택되지 않았을 때 나타낼 blank변수를 선언합니다. 그리고 props로 넘겨받은 isSelected가 true이면 details, false면 blank를 그리도록 작성합니다. 그리고 props로 전달받는 contact의 name과 phone의 초기값은 공란('')으로 정의해둡니다.

ContactDetails.js

import React from 'react';

export default class ContactDetails extends React.Component {
    render() {

        const details = (	//특정인이 선택되었을 때 표시할 화면요소
            <div>
                <p>{this.props.contact.name}</p>
                <p>{this.props.contact.phone}</p>
            </div>
        );
        const blank = (<div>Not Selected</div>); //아무도 선택되지 않았을 때 표시할 화면요소
        return (
            <div>
                <h2>Details</h2>
                {this.props.isSelected ? details : blank}
            </div>
        )
    }
}

ContactDetails.defaultProps = {
    contact: {
        name: '',
        phone: ''
    }
};
반응형
반응형

App.js

import React from 'react';
import './App.css';
import Contact from './Contact';


function App() {
  return (
    <Contact />
  );
}

export default App;

 

ContactInfo.js

import React, { Component } from 'react';

export default class ContactInfo extends React.Component {
    render() {
        return (
            <div onClick={this.props.onClick}>{this.props.contact.name}</div>   // onClick 을 Props로 받음
        );
    }
}

 

Contact.js

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

export default class Contact extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            selectedKey: -1,
            keyword: '',
            contactData: [{
                name: 'Abet',
                phone: '010-000-001'
            }, {
                name: 'Betty',
                phone: '010-000-002'
            }, {
                name: 'Charlie',
                phone: '010-000-003'
            }, {
                name: 'David',
                phone: '010-000-004'
            }]
        }
        this.handleChange = this.handleChange.bind(this);
        this.handleClick = this.handleClick.bind(this);
    }

    handleChange(e) {
        this.setState({
            keyword: e.target.value
        })
    }

    handleClick(key) {
        this.setState({
            selectedKey: key
        });
        console.log(key, 'is selected');
    }

    render() {
        const mapToComponents = (data) => {     // 배열형 data 받아들임
            data.sort();    // 오름차순정렬
            data = data.filter((_contact) => {
                return _contact.name.toLowerCase().indexOf(this.state.keyword.toLowerCase()) > -1;   // contact 배열에서 keyword를 포함하는(indexOf) 요소를 찾아서(-1 보다 큰..) 반환 (모두 소문자로 변환하여 비교)
            })
            return data.map((_contact, i) => {   // 배열의 1개 원소 _contact 와 인덱스 i를 넘겨줌
                return (<ContactInfo
                    contact={_contact}
                    key={i}
                    onClick={() => this.handleClick(i)} // 화살표 함수로 함수를 호출해야 작동함
                />);
            });
        };

        return (
            <div>
                <h1>Contacts</h1>
                <input
                    name="keyword"
                    placeholder="Search"
                    value={this.state.keyword}
                    onChange={this.handleChange}
                />
                <div>{mapToComponents(this.state.contactData)}</div>               
            </div>
        );
    }
}
반응형

'Programming > React' 카테고리의 다른 글

React강좌2, 전화번호부 - 3.Create 기능  (0) 2020.08.03
React강좌2, 전화번호부 - 2.Detail 보기 기능  (0) 2020.08.01
React기초- 9.Delete  (0) 2020.07.29
React기초- 8.Update  (0) 2020.07.29
React기초- 7.Create  (0) 2020.07.29
반응형

마지막으로 Delete 기능을 알아보겠습니다.

Delete 기능은 App.js 내부에서만 다루며, 그 중에서도 Control 컴포넌트 안에서만 구현하는 것으로 충분합니다.

 

App.js

import React, { Component } from 'react';
import Subject from './components/Subject';
import TOC from './components/TOC';
import ReadContent from './components/ReadContent';
import CreateContent from './components/CreateContent';
import UpdateContent from './components/UpdateContent';
import Control from './components/Control';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.max_content_id = 3;
    this.state = {
      mode: 'welcome',
      selected_content_id: 2,
      welcome: { title: 'welcom', desc: 'Hello, React!!' },
      subject: { title: 'WEB', sub: 'world wide web!' },
      contents: [
        { id: 1, title: 'HTML', desc: 'HTML is HyperText ...' },
        { id: 2, title: 'CSS', desc: 'CSS is for design' },
        { id: 3, title: 'Javascript', desc: 'JavaScript is for interactive' }
      ]
    }
  }

  getReadContent() {
    var i = 0;
    while (i < this.state.contents.length) {
      var data = this.state.contents[i];
      if (data.id === this.state.selected_content_id) {
        return data;
        break;
      }
      i = i + 1;
    }
  }

  getContent() {
    var _title, _desc, _article = null;
    if (this.state.mode === 'welcome') {
      _title = this.state.welcome.title;
      _desc = this.state.welcome.desc;
      _article = <ReadContent title={_title} desc={_desc}></ReadContent>
    } else if (this.state.mode === 'read') {
      var _content = this.getReadContent();
      _article = <ReadContent title={_content.title} desc={_content.desc}></ReadContent>
    } else if (this.state.mode === 'create') {
      _article = <CreateContent onSubmit={function (_title, _desc) {
        this.max_content_id = this.max_content_id + 1;
        var _contents = Array.from(this.state.contents)
        _contents.push({ id: this.max_content_id, title: _title, desc: _desc })
        this.setState({
          contents: _contents,
          mode: 'read',
          selected_content_id: this.max_content_id
        })
      }.bind(this)}></CreateContent>
    } else if (this.state.mode === 'update') {
      _content = this.getReadContent();
      _article = <UpdateContent data={_content} onSubmit={function (_id, _title, _desc) {
        var _contents = Array.from(this.state.contents)
        var i = 0;
        while (i < _contents.length) {
          if (_contents[i].id === _id) {
            _contents[i] = { id: _id, title: _title, desc: _desc }
            break;
          }
          i = i + 1;
        }
        this.setState({
          contents: _contents,
          mode: 'read'
        })
      }.bind(this)}></UpdateContent>
    }
    return _article;
  }
  render() {

    return (
      <div className="App">
        <Subject
          title={this.state.subject.title}
          sub={this.state.subject.sub}
          onChangePage={function () {
            this.setState({ mode: 'welcome' });
          }.bind(this)}
        />

        <TOC
          onChangePage={function (id) {
            this.setState({
              mode: 'read',
              selected_content_id: Number(id)
            });
          }.bind(this)}
          data={this.state.contents} />

        <Control
          onChangeMode={function (_mode) {
            if (_mode === 'delete') {           //delete 모드로 진입
              if (window.confirm('really?')) {  // 경고창에서 확인한 경우에만 실행
                var _contents = Array.from(this.state.contents);  // 배열 복제
                var i = 0;
                while (i < this.state.contents.length) {
                  if (_contents[i].id === this.state.selected_content_id) { // i번째 요소가 selected_content_id와 같을 경우
                    _contents.splice(i, 1); // i번째 요소로부터 1개 삭제(본인)
                    break;
                  }
                  i = i + 1;
                }
                this.setState({
                  mode: 'welcome',    //삭제한 후에는 welcome화면으로
                  contents: _contents // 새로운 배열을 기존 배열로 덮어쓰기
                })
              }
            } else {
              this.setState({
                mode: _mode
              });
            }
          }.bind(this)} />

        {this.getContent()}
      </div>
    );
  }
}


export default App;

 

 

반응형

'Programming > React' 카테고리의 다른 글

React강좌2, 전화번호부 - 2.Detail 보기 기능  (0) 2020.08.01
React강좌2, 전화번호부 - 1.Search기능  (0) 2020.08.01
React기초- 8.Update  (0) 2020.07.29
React기초- 7.Create  (0) 2020.07.29
React기초- 6.이벤트  (0) 2020.07.28

+ Recent posts