[React] React 실습 및 참고

2021. 1. 8. 16:04React/React

1. Input 상태 관리

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

class App extends Component {
  render() {
    return (
      <div>
        <PhoneForm />
      </div>
    );
  }
}

export default App;
import React, { Component } from 'react';

class PhoneForm extends Component {

    state = {
        name: '',
        phone: ''
    }

    handleChange = (e) => {
        this.setState({
            [e.target.name]: e.target.value
        });
    }

    render() {
        return (
            <form>
                <input name="name" placeholder="이름" onChange={this.handleChange} value={this.state.name}/>
                <input name="phone" placeholder="전화번호" onChange={this.handleChange} value={this.state.phone}/>
                
                <div>
                    {this.state.name} {this.state.phone}
                </div>
            </form>
        );
    }
}

export default PhoneForm;

 

 

 

2. 배열 데이터

1) 배열 데이터 삽입

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

class App extends Component {

  id = 0; // 고유 키 값

  state = {
    information: [],
  }

  handleCreate = (data) => {
    const { information } = this.state;
    this.setState({
      information: information.concat({
        ...data,
        id: this.id++
      })
    });
  }

  render() {
    return (
      <div>
        <PhoneForm onCreate={this.handleCreate}/>
        {JSON.stringify(this.state.information)}
      </div>
    );
  }
}

export default App;

handleCreate 함수의 경우 다른 방식으로 배열에 저장할 수 있다. Object.assign의 파라미터의 경우, 빈 공간('{ }')에 data와 id 배열을 넣는다는 의미로 생각하면 쉽게 이해할 수 있다. 또한 id의 값은 렌더링하는 값이 아니기에 state에 넣지 않아도 된다.

...

  handleCreate = (data) => {
    const { information } = this.state;
    this.setState({
      information: information.concat(Object.assign({}, data, { 
        id: this.id++ 
      }))
    })
  }
...
import React, { Component } from 'react';

class PhoneForm extends Component {

    state = {
        name: '',
        phone: ''
    }

    handleChange = (e) => {
        this.setState({
            [e.target.name]: e.target.value
        });
    }

    handleSubmit = (e) => { // 등록 버튼이 눌릴 때 마다 페이지가 리로딩하지 않도록 설정
        e.preventDefault();
        this.props.onCreate(this.state);
        this.setState({
            name: '',
            phone: ''
        })
    }

    render() {
        return (
            <form onSubmit={this.handleSubmit}>
                <input name="name" placeholder="이름" onChange={this.handleChange} value={this.state.name}/>
                <input name="phone" placeholder="전화번호" onChange={this.handleChange} value={this.state.phone}/>
                <button type="submit">등록</button>
            </form>
        );
    }
}

export default PhoneForm;

2) 배열 데이터 렌더링

map은 배열을 특정함수를 사용해서 전체적으로 변환해주고 싶을 때 사용한다. 기본적인 map 사용법 예시는 다음과 같다.

const numbers = [1,2,3,4,5];
const squared = numbers.map(n => n * n);
squared

 

map을 프로젝트에 적용한다면 다음과 같다.

import React, { Component } from 'react';

class PhoneInfo extends Component {
    render() {
        const { name, phone, id} = this.props.info;

        const style = {
            border: '1px solid black',
            padding: '8px',
            margin: '8px',
        }
        return (
            <div style={style}>
                <div><b>{name}</b></div>
                <div><b>{phone}</b></div>
            </div>
        );
    }
}

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

class PhoneInfoList extends Component {

    static defaultProps = {
        data: []
    }

    render() {
        const { data } = this.props;
        const list = data.map(
            info => (<PhoneInfo info={info} key={info.id} />)
        )
        return (
            <div>
                {list}
            </div>
        );
    }
}

export default PhoneInfoList;

※ key 값은 내부적으로 제거하거나 업데이트하거나 추가할 때 그 작업을 효율적으로 하기위해 사용되는 값이다.

import React, { Component } from 'react';

class PhoneForm extends Component {

    state = {
        name: '',
        phone: ''
    }

    handleChange = (e) => {
        this.setState({
            [e.target.name]: e.target.value
        });
    }

    handleSubmit = (e) => { // 등록 버튼이 눌릴 때 마다 페이지가 리로딩하지 않도록 설정
        e.preventDefault();
        this.props.onCreate(this.state);
        this.setState({
            name: '',
            phone: ''
        })
    }

    render() {
        return (
            <form onSubmit={this.handleSubmit}>
                <input name="name" placeholder="이름" onChange={this.handleChange} value={this.state.name}/>
                <input name="phone" placeholder="전화번호" onChange={this.handleChange} value={this.state.phone}/>
                <button type="submit">등록</button>
            </form>
        );
    }
}

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

class App extends Component {

  id = 0; // 고유 키 값

  state = {
    information: [],
  }

  handleCreate = (data) => {
    const { information } = this.state;
    this.setState({
      information: information.concat({
        ...data,
        id: this.id++
      })
    });
  }

  render() {
    return (
      <div>
        <PhoneForm onCreate={this.handleCreate}/>
        <PhoneInfoList data={this.state.information}/>
      </div>
    );
  }
}

export default App;

4) 배열 데이터 삭제

Javascript 내부 함수(.slice, .filter) 를 이용하여 삭제를 할 수 있다. 기본적인 사용법은 다음과 같다.

const numbers = [1,2,3,4,5];
numbers.slice(0,2); // 인덱스 0 ~ 2 전까지
numbers.slice(0,3); // 인덱스 0 ~ 3 전까지
numbers.slice(0,2).concat(numbers.slice(3,5) // 1,2,4,5
[
  ...numbers.slice(0,2),
  10,
  ...numbers.slice(3.5)
]


numbers.filter(n => n > 3);
numbers.filter(n => n !== 3);

프로젝트에 적용한다면 다음과 같다.

import React, { Component } from 'react';

class PhoneInfo extends Component {

    handleRemove = () => {
        const { info, onRemove } = this.props;
        onRemove(info.id);
    }

    render() {
        const { name, phone } = this.props.info;

        const style = {
            border: '1px solid black',
            padding: '8px',
            margin: '8px',
        }
        return (
            <div style={style}>
                <div><b>{name}</b></div>
                <div><b>{phone}</b></div>
                <button onClick={this.handleRemove}>삭제</button>
            </div>
        );
    }
}

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

class PhoneInfoList extends Component {

    static defaultProps = {
        data: []
    }

    render() {
        const { data, onRemove } = this.props;
        const list = data.map(
            info => (<PhoneInfo onRemove={onRemove} info={info} key={info.id} />)
        )
        return (
            <div>
                {list}
            </div>
        );
    }
}

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

class App extends Component {

  id = 0; // 고유 키 값

  state = {
    information: [],
  }

  handleCreate = (data) => {
    const { information } = this.state;
    this.setState({
      information: information.concat({
        ...data,
        id: this.id++
      })
    });
  }

  handleRemove = (id) => {
    const { information } = this.state; // 비구조화 할당
    this.setState({
      information: information.filter(info => info.id !== id)
    });
  }

  render() {
    return (
      <div>
        <PhoneForm onCreate={this.handleCreate}/>
        <PhoneInfoList data={this.state.information} onRemove={this.handleRemove}/>
      </div>
    );
  }
}

export default App;

5) 배열 데이터 수정

import React, { Component, Fragment } from 'react';

class PhoneInfo extends Component {

    state = {
        editing: false,
        name: '',
        phone: ''
    }

    handleRemove = () => {
        const { info, onRemove } = this.props;
        onRemove(info.id);
    };

    handleToggleEdit = () => {
        const { info, onUpdate } = this.props;
        if(this.state.editing) {
            onUpdate(info.id, {
                name: this.state.name,
                phone: this.state.phone
            });
        } else {
            this.setState({
                name: info.name,
                phone: info.phone
            })
        }
        this.setState({
            editing: !this.state.editing,
        });
    };


    handleChange = (e) => {
        this.setState({
            [e.target.name]: e.target.value
        });
    }

    render() {
        const { name, phone } = this.props.info;
        const { editing } = this.state;

        const style = {
            border: '1px solid black',
            padding: '8px',
            margin: '8px',
        };

        return (
            <div style={style}>
                {
                    editing ? (
                        <Fragment>
                            <div><input name="name" onChange={this.handleChange} value={this.state.name}/></div>
                            <div><input name="phone" onChange={this.handleChange} value={this.state.phone}/></div>
                        </Fragment>
                    ) : (
                        <Fragment>
                            <div><b>{name}</b></div>
                            <div><b>{phone}</b></div>
                        </Fragment>
                    )
                }
                
                <button onClick={this.handleRemove}>삭제</button>
                <button onClick={this.handleToggleEdit}>
                    { editing ? '적용' : '수정' }
                </button>
            </div>
        );
    }
}

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

class PhoneInfoList extends Component {

    static defaultProps = {
        data: []
    }

    render() {
        const { data, onRemove, onUpdate } = this.props;
        const list = data.map(
            info => (<PhoneInfo onUpdate={onUpdate} onRemove={onRemove} info={info} key={info.id} />)
        )
        return (
            <div>
                {list}
            </div>
        );
    }
}

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

class App extends Component {

  id = 0; // 고유 키 값

  state = {
    information: [],
  }

  handleCreate = (data) => {
    const { information } = this.state;
    this.setState({
      information: information.concat({
        ...data,
        id: this.id++
      })
    });
  }

  handleRemove = (id) => {
    const { information } = this.state; // 비구조화 할당
    this.setState({
      information: information.filter(info => info.id !== id)
    });
  }

  handleUpdate = (id, data) => {
    const { information } = this.state;
    this.setState({
      information: information.map(
        info => {
          if(info.id === id) {
            return {
              id,
              ...data,
            };
          } 
          return info;
        }
      )
    })
  }

  render() {
    return (
      <div>
        <PhoneForm onCreate={this.handleCreate}/>
        <PhoneInfoList data={this.state.information} onUpdate={this.handleUpdate} nRemove={this.handleRemove}/>
      </div>
    );
  }
}

export default App;

6) 성능 최적화

import React, { Component, Fragment } from 'react';

class PhoneInfo extends Component {

...

    shouldComponentUpdate(nextProps, nextState) {
        if(this.state !== nextState) {
            return true;
        } 
        return this.props.info !== nextProps.info;
    }
...

7) 이름 검색

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

class App extends Component {

  id = 3; // 고유 키 값

  state = {
    information: [
      {
        id: 0,
        name: '홍길동',
        phone: '010-0000-0001'
      },
      {
        id: 1,
        name: '고길동',
        phone: '010-0000-0002'
      },
      {
        id: 2,
        name: '테스트',
        phone: '010-0000-0003'
      }
    ],
    keyword: '',
  }

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

  handleCreate = (data) => {
    const { information } = this.state;
    this.setState({
      information: information.concat({
        ...data,
        id: this.id++
      })
    });
  }

  handleRemove = (id) => {
    const { information } = this.state; // 비구조화 할당
    this.setState({
      information: information.filter(info => info.id !== id)
    });
  }

  handleUpdate = (id, data) => {
    const { information } = this.state;
    this.setState({
      information: information.map(
        info => {
          if(info.id === id) {
            return {
              id,
              ...data,
            };
          } 
          return info;
        }
      )
    })
  }

  render() {
    return (
      <div>
        <PhoneForm onCreate={this.handleCreate}/>
        <input placeholder="검색" onChange={this.handleChange} value={this.state.keyword} />
        <PhoneInfoList data={this.state.information.filter(
          info => info.name.indexOf(this.state.keyword) > -1
        )} 
        
        onUpdate={this.handleUpdate} nRemove={this.handleRemove}/>
      </div>
    );
  }
}

export default App;

8) DOM 직접 접근 (Ref)

focus, 특정 DOM의 크기, 스크롤 설정, 외부 라이브러리(차트) 등 직접적으로 DOM에 접근할 때 사용한다.

import React, { Component } from 'react';

class PhoneForm extends Component {

    input = React.createRef();

    state = {
        name: '',
        phone: ''
    }

    handleChange = (e) => {
        this.setState({
            [e.target.name]: e.target.value
        });
    }

    handleSubmit = (e) => { // 등록 버튼이 눌릴 때 마다 페이지가 리로딩하지 않도록 설정
        e.preventDefault();
        this.props.onCreate(this.state);
        this.setState({
            name: '',
            phone: ''
        })
        this.input.current.focus();
    }

    render() {
        return (
            <form onSubmit={this.handleSubmit}>
                <input name="name" placeholder="이름" onChange={this.handleChange} value={this.state.name} ref={this.input}/>
                <input name="phone" placeholder="전화번호" onChange={this.handleChange} value={this.state.phone}/>
                <button type="submit">등록</button>
            </form>
        );
    }
}

export default PhoneForm;

 

 

 

3. 추가 참고

Redux, React Router, TDD, Design Pattern


[참고] velopert.com/reactjs-tutorials

[참고] www.facebook.com/velopert/

[참고] tech.madup.com/atomic-design/

[참고] delivan.dev/react/programming-patterns-with-react-hooks-kr/

[참고] kyounghwan01.github.io/blog/React/container-presenter-dessign-pattern/

[참고] velog.io/@mollang/19.12.05-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4

728x90

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

[React] React 작업 환경 설정  (0) 2021.01.08
[React] LifeCycle API  (0) 2021.01.08
[React] Props, State  (0) 2021.01.08
[React] JSX 기본 문법, 스타일  (0) 2021.01.08