반응형

1. next.config.mjs 수정

 

- 위와 같은 package.json 이 있다고 할 때, "npm run build" 를 하면 .next 폴더가 만들어지는데, next.config.mjs파일을 아래와 같이 수정해서 out파일을 배포하도록 설정을 바꿔줘야함.

 - 이미지 등의 경로를 제대로 인식할 수 있도록 basePath를 설정합니다.  [Next.js 의 모든 URL 앞에 추가되는 기본 경로를 설정]

/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'export',
  basePath: '',
  images: {
    unoptimized: true,
  },
};

export default nextConfig;

 

예전에는 next build 후 out폴더에 static 파일들을 생성하도록 next export 명령어도 했어야 했던 것 같은데, 이번 14버전에서는 next export 대신 config파일에서 output: 'export' 를 설정해주는 것만으로도 작동합니다.

 

.next폴더와 out폴더 두개가 모두 생성되는데, netlify에 배포하는 폴더는 out 폴더입니다. 

 

2. Nelify플러그인 설치

무슨 역할을 하는지는 모르겠지만, 이것도 설치해야한다고 합니다.

npm install @netlify/plugin-nextjs

 

3. netlify.toml 생성

[build]
  command = "npm run build"
  publish = "out"

[[plugins]]
  package = "@netlify/plugin-nextjs"

 

 

이미지는 나왔는데, CSS가 아직 제대로 적용이 안되었네요. 조금 더 검토해봐야겠습니다.

 

오늘은 여기까지!

반응형
반응형

비주얼 테스트 도구인 스토리북에 대해 알아보겠습니다. 

 

1. 설치

npx -p @storybook/cli sb init

.storybook폴더가 자동 생성되고, 그 안에 story파일을 로딩해주는 main.js 와 preview.js 파일이 있습니다. main.js에는 ...story.js파일을 자동 추가/로딩해주는 부분과 addon 설정부분이 있습니다. (preview.js는 아직 잘 모르겠습니다.ㅠㅠ)

package.json에도 storybook 스크립트가 추가되었습니다. (모두 이전 버전에서 수동 생성해주던 부분임...)

 

2. Sample 컴포넌트 작성 <./src/components/Input.jsx 파일 생성>

기존 컴포넌트들이 있으면 활용해도 되지만, 저는 새로 만든 프로젝트에서 테스트할 용도로 간단한? 컴포넌트 하나를 만들겠습니다.

import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';

class Input extends PureComponent {
    constructor(props) {
        super(props);
        this.setRef = this.setRef.bind(this);
        this.handleChange = this.handleChange.bind(this);
    }
    handleChange(e) {
        const { name, onChange } = this.props;
        if(onChange) {
            onChange(name, e.target.value);
        }
    }
    componentDidMount(){
        if(this.props.autoFocus){
            this.ref.focus();
        }
    }
    componentDidUpdate(){
        if(this.props.autoFocus) {
            this.ref.focus();
        }
    }
    setRef(ref){
        this.ref = ref;
    }
    render() {
        const { errorMessage, label, name, value, type, onFocus } =this.props;
        return (
            <label>
                {label}
                <input
                  id={`input_${name}`}
                  ref={this.setRef}
                  onChange={this.handleChange}
                  onFocus={onFocus}
                  value={value}
                  type={type}
                />
                {errorMessage && <span className="error">{errorMessage}</span>}                
            </label>
        );
    }
}

Input.propTypes = {
    type: PropTypes.oneOf(['text','number','price']),
    name: PropTypes.string.isRequired,
    value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
    errorMessage: PropTypes.string,
    label: PropTypes.string,
    onChange: PropTypes.func,
    onFocus: PropTypes.func,
    autoFocus: PropTypes.bool,
};
Input.defaultProps={
    onChange: () => {},
    onFocus: () => {},
    autoFocus: false,
    type: 'text',
};

export default Input;

 

3. 스토리 파일 생성(./src/stories/Input.stories.js)

위에서 만든 컴포넌트를 스토리북으로 넘겨줄 story파일을 만들겠습니다.

import React from 'react';
import { storiesOf } from '@storybook/react';
import Input from '../components/Input';

storiesOf('Input', module).add('기본설정', () => <Input />);

src/components/Input.jsx 와 src/stories/Input.stories.js 파일 구조. 선호하는 방식에 따라 변경 가능하다.

4. 실행 및 확인

yarn storybook

 

샘플 컴포넌트들이 하단에 나오고, 새로 생성한 Input 컴포넌트가 가장 위에 표시되는 모습

 


 

스토리북이 버전이 올라가면서 설정할 것도 별로 없어지고 편리해졌네요. story파일 자동으로 추가되도록 스크립트도 이미 생성이 되어있고, storybook 명령도 package.json파일에 자동 등록되고, 무엇보다도 개별 story를 등록해줄 필요도 없어진 것 같아 훨씬 편하게 사용할 수 있게 되었네요. 그냥 설치하고 xxx.stories.js 파일만 생성해주면 자동 등록되네요. 예전 책 "리액트 프로그래밍 정석" 으로 따라하다 잘 안되서 최신 사용법을 찾아본 소감이었습니다.

 

~끝~

반응형
반응형

React하면 아시다시피 frontend 라이브러리라고 하는데요.. React에서 Database의 내용을 가져오려면 어떻게 해야할까요? 오늘 실습은 이 질문에서 시작했습니다. PHP, JSP, Django 등 여러가지 backend 를 이용하여 Database를 접속할 수 있을텐데요. 저는 가장 간단할 것으로 예상되는 apache, php, mysql의 조합을 이용하여 php API를 구성하는 방법으로 구성해볼까 합니다.

 

그래서 필요한 것은 간단한 php API. 아래 코드를 test.php라는 파일로 작성하여 아파치 루트 다큐먼트 경로(예: C:\xampp\htdocs)에 저장합니다. (Database는 사전에 적당한 값으로 구성하셔야합니다.)

 

test.php

<?php
    $host = 'localhost';
    $user = 'root';
    $pw = '1234';
    $dbName = 'TestDB';
    
    $conn = mysqli_connect($host, $user, $pw, $dbName);
        
    $sql = "SELECT * FROM members";
    $result = mysqli_query($conn, $sql);    
    $row = mysqli_fetch_array($result);

    $list_array = array("name" =>$row['name'],
                        "email" =>$row['email'],
                        "phone" => $row['phone']);

    $result_array = json_encode($list_array);
    
    echo $result_array;
    
?>

이 파일은 localhostTestDB에 접속하여 members테이블의 모든 row를 추출해냅니다. 그리고

결과를 $row에 저장하고, 이를 다시

배열형태로 $list_array에 할당합니다.

다시한번 json형태로 인코딩하여 $result_array에 할당합니다.

echo 함수는 이 파일의 결과값으로 $result_array를 출력하게 합니다.

 

Backend에서의 동작은 구현했습니다. 80번 포트에서요. 이번에는 개발환경인 3000 포트에서 구동하는 React App을 구성할 차례인데요, 포트가 달라지면 쿠키라던지 데이터 전달에 제한을 받게되어 원래대로라면 여러가지 다른 방법을 시도해야 한다고 합니다. 그래서 proxy라는 걸 이용하는데요. package.json파일에 아래 한줄만 추가해주면 됩니다.

"proxy": "http://127.0.0.1:80",

위 코드를 package.json 내부 적당한 위치에 아래와 같이 넣어줍니다. 그러면 localhost의 접근 시도에 대해 proxy 주소로 자동으로 넘겨주고, 추후 구성할 React 앱에서도 라우팅 부분을 간소화할 수 있습니다.

 

App.js

아래와 같이 Hello라는 텍스트와 클릭 가능한 단순 버튼을 추가하겠습니다. 버튼은 클릭시 test.php를 실행하여 DB정보를 json형태로 가져오게 합니다. 보시는 바와 같이 원래대로라면 fetch('http://127.0.0.1:80/test.php')와 같이 작성해야하나 훨씬 간소하게 구성된 것을 볼 수 있습니다.

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

class App extends Component {
  render(){
    return (
      <div className="App">
        Hello
        <input type="button" value="get data" onClick={
          function(){
            fetch('/test.php')
            .then(function(response){
              return response.json();
            })
            .then(function(json){console.log(json);})
          }.bind(this)
        }></input>
      </div>
    );
  }
}

export default App;

 

이제 xampp도 실행하고, npm start로 리액트도 실행해 봅시다. localhost/test.php를 입력하면 DB 데이터가 json형태로 잘 출력됨을 볼 수 있습니다.

 

- 끝 -

 

반응형

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

PHP와IIS 연동하기  (0) 2020.10.24
반응형

오늘은 React에서 Highchart 사용하여 아래와 같은 BAR 차트를 한번 그려보려고 합니다. 

1. 설치

우선 create-react-app으로 리액트 환경을 구축하고, Highcharts 와 highcharts-react-official을 설치해 줍니다.

create-react-app .	// '.' 을 입력하면 현재 폴더에 리액트를 설치합니다.
npm install highcharts --save
npm install highcharts-react-official --save

 

2. High.js (Highchart 컴포넌트) 작성

이번 예제에서는 App.js에서 데이터를 props로 전달해주고, Highchart 컴포넌트에서 이를 받아 그래프를 그리도록 구성하겠습니다. 따라서 그래프를 그리는 High.js 를 작성합니다. 

import React, { Component, Fragment } from "react";
import Highcharts from "highcharts";
import HighchartsReact from "highcharts-react-official";

class High extends Component {
    render() {
        const series2 = this.props.data;    //App.js에서 데이터를 보내줄 예정
        const options = {
            chart: {
                type: 'bar'		// bar차트. 아무 설정이 없으면 line chart가 된다.
            },
            title: {
                text: 'My first bar chart'
            },
            credits: {
                enabled: false
            },
            xAxis: {
                type: 'category'
            },
            legend: {
                reversed: true
            },
            plotOptions: {
                series: {
                    stacking: 'normal',
                    dataLabels: {
                        enabled: true,
                        format: "<b>{point.y}</b>",
                    }
                }
            },
            series: [{ name: "data", data: series2 }]

        }
        return (
            <Fragment>
                <HighchartsReact highcharts={Highcharts} options={options} />
            </Fragment>
        );
    }
}
export default High;

 

3. App.js 수정

App.js에서는 위에서 작성한 High.js를 임포트해주고, data를 state로 선언해준 다음 <Highcharts>의 props로 전달해줍니다.

import React, { useState, useEffect, Component } from 'react';
import HighCharts from './High';
import './App.css';


class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: [40000, 50000, 60000, 70000, 80000, 100000, 120000, 150000]
    }
  }
  render() {
    return (
      <div className="App">
        <HighCharts
          data={this.state.data} />
      </div>
    );
  }
}

export default App;

 

실행하면 맨 윗화면처럼 나오게 될 겁니다. 

잘 되시길....행운을 빕니다.

 

(ps) option - type 값을 변경하여 여러가지 차트를 그릴 수 있습니다.

1. type: 'line'

2. type: 'column'

3. type: 'areaspline'

4. type: 'pie'

5. type:'scatter'

- 끝 -

반응형
반응형

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;

 

반응형

+ Recent posts