반응형

삭제는

  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
반응형

업데이트하는 방법에 대해 알아보겠습니다. 업데이트는 기존의 contents를 불러와서 보여주는 read 기능과 form의 형태로 다시 입력하는 create 기능이 결합된 기능이라고 할 수 있습니다.

 

1. UpdateContent.js

import React, { Component } from 'react';

class UpdateContent extends Component {
    constructor(props) {
        super(props);
        this.state = {
            id: this.props.data.id,
            title: this.props.data.title,
            desc: this.props.data.desc
        }
        this.inputFormHandler = this.inputFormHandler.bind(this);   // bind를 construct에서 구현하여 추후 계속 bind를 물고가는 부분을 제거
    }
    inputFormHandler(e) {
        this.setState({ [e.target.name]: e.target.value }); // target name값에 따라 input / textarea 를 각각 업데이트함
    }
    render() {
        return (
            <div>
                <article>
                    <h2>Update</h2>
                    <form action="/create_process" method="post"
                        onSubmit={function (e) {
                            e.preventDefault();
                            this.props.onSubmit(
                                this.state.id,
                                this.state.title,
                                this.state.desc
                            );
                        }.bind(this)}
                    >
                        <input type="hidden" name="id" value={this.state.id}></input>   {/* id와 같이 사용자에게 보여줄 필요 없는 부분 */}
                        {/* onChange는 필요 없음. 현재 프로그래밍의 기조임 */}
                        <p>
                            <input
                                type="text"
                                name="title"
                                placeholder="title"
                                value={this.state.title}
                                onChange={this.inputFormHandler}
                            ></input>
                        </p>
                        <p>
                            <textarea
                                name="desc"
                                placeholder="description"
                                value={this.state.desc}
                                onChange={this.inputFormHandler}
                            ></textarea>
                        </p>
                        <p><input type="submit"></input></p>
                    </form>
                </article>
            </div>
        );
    }
}

export default UpdateContent;

 

2. 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: 'create',
      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() {  //selected_content_id 와 일치하는 content를 찾는 부분만 별도로 작성 ->read / update에서 같이 사용
    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;
    }
  }
  //  render 함수를 새로운 함수로 분리
  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();         //getReadContent()로부터 data 형태로 값을 받음
      _article = <ReadContent title={_content.title} desc={_content.desc}></ReadContent>  // data형태의 값인 _content로부터 title 과 desc를 참조함
    } 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',  // 수정한 다음에는 자동으로 read모드가 되도록
          selected_content_id: this.max_content_id // 선택된 값은 마지막 값이 되도록
        })
      }.bind(this)}></CreateContent>
    } else if (this.state.mode === 'update') { // update 모드 구현
      _content = this.getReadContent();
      _article = <UpdateContent data={_content} onSubmit={function (_id, _title, _desc) {  // getReadContent()로부터 data로 주입시켜준다.
        var _contents = Array.from(this.state.contents) // contents를 복제한 _contents배열 생성
        var i = 0;
        while (i < _contents.length) {                  // _contents 배열만큼 반복
          if (_contents[i].id === _id) {                // _contents의 i번째 요소가 전달받은 _id와 같을 경우
            _contents[i] = { id: _id, title: _title, desc: _desc }  // _contents[i]에 id, title, desc를 교체입력
            break;
          }
          i = i + 1;
        }
        this.setState({
          contents: _contents,
          mode: 'read'  // 수정한 다음에는 자동으로 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) {
            this.setState({
              mode: _mode
            })
          }.bind(this)} />

        {this.getContent()} {/*getContent로부너 _article 변수 입력 */}
      </div>
    );
  }
}


export default App;
반응형

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

React강좌2, 전화번호부 - 1.Search기능  (0) 2020.08.01
React기초- 9.Delete  (0) 2020.07.29
React기초- 7.Create  (0) 2020.07.29
React기초- 6.이벤트  (0) 2020.07.28
React기초-5.state2  (0) 2020.07.27
반응형

 

이번에는 아이템 추가하는 기능을 구현해보도록 하겠습니다.

 

1. 신규요소 (Control.js) 추가

각 링크에는 onClick 이벤트 요소를 선언하고 onChangeMode함수와 연결을 해 줍니다. 이때, 모드를 인자로 넘겨줍니다.

import React, { Component } from 'react';

class Control extends Component {
    render() {
        return (
            <ul>
                <li>
                    <a href="/create" onClick={function (e) {   // onChangeMode 함수 연결
                        e.preventDefault();
                        this.props.onChangeMode('create');
                    }.bind(this)}>create</a>
                </li>
                <li>
                    <a href="/update" onClick={function (e) {   // onChangeMode 함수 연결
                        e.preventDefault();
                        this.props.onChangeMode('update');
                    }.bind(this)}>update</a>
                </li>
                <li>
                    <input onClick={function (e) {      // onChangeMode 함수 연결
                        e.preventDefault();
                        this.props.onChangeMode('delete');
                    }.bind(this)} type="button" value="delete"></input>
                </li>
            </ul>
        );
    }
}

export default Control;

2. 신규요소(CreateContent.js) 추가

신규 생성시 입력폼을 작성합니다. 폼은 아래 그림의 아래부분과 같이 구성되어 있습니다. 이때 onSubmit 함수로 title 과 desc 값이 전달될 수 있도록 아래와 같이 코딩을 합니다.

import React, { Component } from 'react';

class CreateContent extends Component {
    render() {
        return (
            <div>
                <article>
                    <h2>Create</h2>
                    <form action="/create_process" method="post"
                        onSubmit={function (e) {
                            e.preventDefault();
                            this.props.onSubmit(
                                e.target.title.value,
                                e.target.desc.value
                            );
                        }.bind(this)}
                    >
                        <p><input type="text" name="title" placeholder="title"></input></p>
                        <p><textarea name="desc" placeholder="description"></textarea></p>
                        <p><input type="submit"></input></p>
                    </form>
                </article>
            </div>
        );
    }
}

export default CreateContent;

3. App.js 수정

  - 새로 작성한 Control, CreateContent 컴포넌트를 임포트하고,

  - welcome, read, create, ... 모드에 따라 content를 다르게 보여주도록 _article 변수를 활용하도록 코딩을 수정하고,

  - CreateContent 컴포넌트(2번)에서 title, desc 입력 후 Submit 시 기존 content에 데이터 추가되는 부분 수정한다.

import React, { Component } from 'react';
import Subject from './components/Subject';
import TOC from './components/TOC';
import ReadContent from './components/ReadContent'; // Content -> ReadContent로 변경
import CreateContent from './components/CreateContent'; // CreateContent 추가
import Control from './components/Control';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.max_content_id = 3;  // 초기값으로 content의 값과 같이 설정함. ui와 전혀 상관이 없는 속성은 state의 외부에 선언
    this.state = {
      mode: 'create',
      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' }
      ]
    }
  }
  render() {
    var _title, _desc, _article = null;   // _article 선언. mode(welcome, read, create) 에 따라서 다른 출력
    if (this.state.mode === 'welcome') {
      _title = this.state.welcome.title;
      _desc = this.state.welcome.desc;
      _article = <ReadContent title={_title} desc={_desc}></ReadContent>  // welcome 모드에서의 표시
    } else if (this.state.mode === 'read') {
      var i = 0;
      while (i < this.state.contents.length) {
        var data = this.state.contents[i];
        if (data.id === this.state.selected_content_id) {
          _title = data.title;
          _desc = data.desc;
          break;
        }
        i = i + 1;
      }
      _article = <ReadContent title={_title} desc={_desc}></ReadContent>  // read 모드에서의 표시
    } else if (this.state.mode === 'create') {
      // 'create'모드일 경우 <CreateContent> 요소를 표시
      _article = <CreateContent onSubmit={function (_title, _desc) {      // _title, _desc를 인자로 받는다.
        this.max_content_id = this.max_content_id + 1;
        var newContents = Array.from(this.state.contents)                // 기존 content 복사하여 새 배열 생성
        newContents.push({ id: this.max_content_id, title: _title, desc: _desc })         // 기존 배열에 새로운 값 추가        
        this.setState({
          contents: newContents   // 기존 contents 값을 _contents 값으로 교체 대입해준다.
        })
      }.bind(this)}></CreateContent>
    }
    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) {  // 모드를 인자로 받아서 전달해준다.
            this.setState({
              mode: _mode
            })
          }.bind(this)} />

        {/*<ReadContent title={_title} desc={_desc} />   삭제*/}
        {_article} {/* content를 create/read 에 따라 다르게 보여줄 새 요소 */}
      </div>
    );
  }
}


export default App;

 

4. TOC.js 수정

shouldComponentUpdate() 함수를 이용하여, 매 변경시마다 무조건 렌더링 되는것을 막는 코드를 추가합니다.

import React, { Component } from 'react';

class TOC extends Component {
    // 매 변경시마다 관계없는 요소들을 모두 렌더하는 것을 막아주는 기능 구현
    shouldComponentUpdate(newProps, newState) { // newProps.data는 바뀐 데이터, this.props.data는 기존 데이터
        if (newProps.data === this.props.data) {    // 바뀐 값이 있다면
            return false;                           // 렌더함수 호출 안함
        }                                           // 그 이외에는 
        return true;                                // 렌더함수 호출

    }
    render() {
        var lists = [];
        var data = this.props.data;
        var i = 0;
        while (i < data.length) {
            lists.push(
                <li key={data[i].id}>
                    <a
                        href={"/content/" + data[i].id}
                        data-id={data[i].id}
                        onClick={function (e) {
                            e.preventDefault();
                            this.props.onChangePage(e.target.dataset.id);
                        }.bind(this)}
                    >{data[i].title}</a></li>);
            i = i + 1;
        }
        return (
            <div>
                <nav>
                    <ul>
                        {lists}
                    </ul>
                </nav>
            </div>
        );
    }
}

export default TOC;

- 끝 - 

반응형

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

React기초- 9.Delete  (0) 2020.07.29
React기초- 8.Update  (0) 2020.07.29
React기초- 6.이벤트  (0) 2020.07.28
React기초-5.state2  (0) 2020.07.27
React기초-4.state  (0) 2020.07.25
반응형

이번 시간에는 클릭 이벤트에 대해 Content가 변경되도록 App을 구성해보겠습니다.

 

1. 아래 화면에서 WEB을 클릭하면 그 아래 HTML / HTML is HyperText Markup Language 부분이 그에 맞는 내용으로 변경되게 할 것입니다.

 

우선 WEB 부분에 해당하는 <h1> 영역에 링크를 달아줍니다.

 

Subject.js

import React, { Component } from 'react';

class Subject extends Component {
    render() {
        return (
            <div>
                <header>
                    <h1><a href="/">{this.props.title}</a></h1>
                    {this.props.sub}
                </header>
            </div>
        );
    }
}

export default Subject;

 

페이지 첫 로딩시와 WEB을 클릭하면 웰컴페이지가 나타나도록 하기 위해 state에 'mode'를 생성하고,

welcome 모드에서 보여줄 내용을 'welcome' 인자를 생성합니다.

render()함수 내부에 _title, _desc 변수를 선언해주고 모드에 따른 내용이 변경되도록 할당해줍니다.

그리고 <Content> 컴포넌트의 내용에 전달되도록 코드를 수정해줍니다.

 

App.js

<Subject> 태그 내부에 onChangePage 속성을 선언하고 실행할 함수를 정의해줍니다. 이 함수는 mode를 'welcome'으로 변경해줍니다.

import React, { Component } from 'react';
import Subject from './components/Subject';
import TOC from './components/TOC';
import Content from './components/Content';
//import logo from './logo.svg'; 삭제
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      mode: 'read',  // 읽기모드인지 welcome모드인지 구분하는 '모드' 설정
      welcome: { title: 'welcom', desc: 'Hello, React!!' }, // welcome모드에서 표시할 내용 선언
      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' }
      ]
    }
  }
  render() {
    var _title, _desc = null;
    if (this.state.mode === 'welcome') {  // welcome 모드일 경우
      _title = this.state.welcome.title;  // welcome 의 title
      _desc = this.state.welcome.desc;    // welcome 의 desc
    } else if (this.state.mode === 'read') {  // read 모드일 경우
      _title = this.state.contents[0].title;  // 1번째 content의 title: 추후 개선 예정
      _desc = this.state.contents[0].desc;    // 1번째 content의 desc: 추후 개선 예정
    }
    return (
      <div className="App">
        <Subject
          title={this.state.subject.title}
          sub={this.state.subject.sub}
          onChangePage={function () {
            this.setState({ mode: 'welcome' });
          }.bind(this)}
        />

        <TOC data={this.state.contents} />

        <Content title={_title} desc={_desc} /> {/* mode에 따라 타이틀과 desc를 전달받는다. */}
      </div>
    );
  }
}


export default App;

 

Subject.js

<a> 태그 내부에 클릭 이벤트 속성인 onClick으로 onChangePage()함수를 연결해줍니다.

import React, { Component } from 'react';

class Subject extends Component {
    render() {
        return (
            <div>
                <header>
                    <h1><a href="/" onClick={function (e) {
                        e.preventDefault();
                        this.props.onChangePage();
                    }.bind(this)}>{this.props.title}</a></h1>
                    {this.props.sub}
                </header>
            </div>
        );
    }
}

export default Subject;

 

2. 다음으로 HTML / CSS / JavaScript부분을 클릭하면 역시 내용이 변경되도록 해보겠습니다.

 

App.js

<TOC> 태그 내부에 마찬가지로 onChangePage 함수를 선언해줍니다.

import React, { Component } from 'react';
import Subject from './components/Subject';
import TOC from './components/TOC';
import Content from './components/Content';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      mode: 'read',
      selected_content_id: 2,  // 기본적으로 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' }
      ]
    }
  }
  render() {
    var _title, _desc = null;
    if (this.state.mode === 'welcome') {
      _title = this.state.welcome.title;
      _desc = this.state.welcome.desc;
    } else if (this.state.mode === 'read') {  // read 모드일 경우
      var i = 0;                                // 반복을 위한 i 변수를 선언하고
      while (i < this.state.contents.length) {    // i 가 content의 요소 갯수만큼 반복하며
        var data = this.state.contents[i];    // i 번째 content를 data 라고 정의한다.
        if (data.id === this.state.selected_content_id) { // data의 id와 현재 선택된 content의 id가 일치할 경우
          _title = data.title;  // content의 title을 다음 진행(출력)으로 넘겨준다.
          _desc = data.desc;    // content의 desc을 다음 진행(출력)으로 넘겨준다.
          break;
        }
        i = i + 1;
      }
    }
    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) {         // 몇번째 요소인지를 id로 넘겨받아서 실행한다.
            this.setState({
              mode: 'read',                     // 모드를 read로 바꿔준다.
              selected_content_id: Number(id)   // selected_content_id에 넘겨받은 id값을 할당하면서 숫자형으로 변환한다.
            });
          }.bind(this)}
          data={this.state.contents} />

        <Content title={_title} desc={_desc} />
      </div>
    );
  }
}


export default App;

 

TOC.js

<a> 태그 내부에 onClick 이벤트 함수를 지정해줍니다.

import React, { Component } from 'react';

class TOC extends Component {
    render() {
        var lists = [];
        var data = this.props.data;
        var i = 0;
        while (i < data.length) {
            lists.push(
                <li key={data[i].id}>
                    <a
                        href={"/content/" + data[i].id}
                        data-id={data[i].id}       // 몇번째 요소의 이벤트인지를 나타내는 id 
                        			  // 'data-XXX'접두사로 시작하는 속성은 e.target.dataset.XXX으로 접근가능하다.
                        onClick={function (e) {                              
                            e.preventDefault();
                            			  // onChangePage()함수를 연결하면서 id값을 넘겨준다.
                            this.props.onChangePage(e.target.dataset.id);  
                        }.bind(this)}
                    >{data[i].title}</a></li>);
            i = i + 1;
        }
        return (
            <div>
                <nav>
                    <ul>
                        {lists}
                    </ul>
                </nav>
            </div>
        );
    }
}

export default TOC;
반응형

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

React기초- 8.Update  (0) 2020.07.29
React기초- 7.Create  (0) 2020.07.29
React기초-5.state2  (0) 2020.07.27
React기초-4.state  (0) 2020.07.25
React기초-3.props  (0) 2020.07.25

+ Recent posts