[스터디]리액트를 다루는 기술 – 컴포넌트

클래스형 컴포넌트

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

class App extends Component {
  render() {
    const name = '리액트';
    return <div className="react">{name}</div>;
  }
}

export default App;

클래스형 컴포넌트

  • state 기능 및 라이프사이클 기능을 사용할 수 있다.
  • render 함수가 있어야 한다.
  • JSX를 반환해야 한다.

함수형 컴포넌트

  • 선언하기 편리하다.
  • 메모리를 적게 사용한다.
  • 빌드한 후 배포할 때 결과물의 파일 크기가 더 작다.
// MyComponent.js
import React from 'react';

const MyComponent = () => {
  return <div>처음 만들어본 컴포넌트</div>;
};

export default MyComponent;

// App.js
import React from 'react';
import MyComponent from './MyComponent';

// 커스텀(?) 태그를 사용할 수 있다.
const App = () => {
  return <MyComponent />;
};

export default App;

props

  • 부모 컴포넌트에서 넘겨주는 값
// MyComponent.js
import React from 'react';

// 부모로부터 받은 값(props)을 출력
const MyComponent = props => {
  return <div>안녕하세요, 제 이름은 {props.name}입니다.</div>;
};

export default MyComponent;

// App.js
import React from 'react';
import MyComponent from './MyComponent';

// MyComponent에 값을 주입한다.
const App = () => {
  return <MyComponent name="홍길동" />;
};

export default App;
// MyComponent.js
import React from 'react';

const MyComponent = props => {
  return <div>안녕하세요, 제 이름은 {props.name}입니다.</div>;
};

// 부모로부터 넘겨받은 값이 없을 때, 디폴트 값을 설정한다.
MyComponent.defaultProps = {
  name: '기본이름'
};

export default MyComponent;


// App.js
import React from 'react';
import MyComponent from './MyComponent';

// MyComponent에 값을 주입하지 않고 넘긴다.
const App = () => {
  return <MyComponent />;
};

export default App;

children

  • 컴포넌트 태그 사이의 내용
// App.js
import React from 'react';
import MyComponent from './MyComponent';

// '리액트'는 MyComponent의 children 값이다.
const App = () => {
  return <MyComponent>리액트</MyComponent>;
};

export default App;


// MyComponent.js
import React from 'react';

const MyComponent = props => {
  return (
    <div>
      안녕하세요, 제 이름은 {props.name}입니다.
      <br />
      children 값은 {props.children}
    </div>
  );
};

MyComponent.defaultProps = {
  name: '기본이름'
};

export default MyComponent;

propTypes를 통한 props 검증

// MyComponents.js
import React from 'react';
import PropTypes from 'prop-types';

const MyComponent = ({ name, children }) => {
    return (
        <div>
            안녕하세요, 제 이름은 {name}입니다.
            <br />
            children 값은 {children}
        </div>
    );
};

MyComponent.defaultProps = {
    name: '기본이름'
};

// name의 타입을 문자열로 지정하였다. 숫자를 넘기면, 브라우저 console에서는 경고가 뜬다. 
// 하지만, 브라우저에서는 실행된다.
MyComponent.propTypes = {
    name: PropTypes.string
};

export default MyComponent;


// App.js
import React from 'react';
import MyComponent from './MyComponent';

// name에 숫자를 넘겨서 console에 경고 메시지가 나온다.
const App = () => {
  return <MyComponent name={3}>리액트</MyComponent>;
};

export default App;
// MyComponent.js
import React from 'react';
import PropTypes from 'prop-types';

const MyComponent = ({ name, favoriteNumber, children }) => {
    return (
        <div>
            안녕하세요, 제 이름은 {name}입니다.
            <br />
            children 값은 {children} 입니다.
            <br />
            제가 좋아하는 숫자는 {favoriteNumber} 입니다.
        </div>
    );
};

MyComponent.defaultProps = {
    name: '기본이름'
};

// isRequired : 필수값으로 설정한다. 미입력시 console에 경고문구 출력
MyComponent.propTypes = {
    name: PropTypes.string,
    favoriteNumber : PropTypes.number.isRequired
};

export default MyComponent;

// App.js
import React from 'react';
import MyComponent from './MyComponent';

const App = () => {
  return <MyComponent name={'1'} favoriteNumber={1}>리액트</MyComponent>;
};

export default App;

클래스형 컴포넌트에서 prop 사용하기

// MyComponent.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';

// 클래스 컴포넌트 형태로 변경하였습니다.
class MyComponent extends Component {
    render() {
        const { name, favoriteNumber, children } = this.props;  // 비구조화 할당
        return (
            <div>
                안녕하세요, 제 이름은 {name}입니다.
                <br />
                children 값은 {children} 입니다.
                <br />
                제가 좋아하는 숫자는 {favoriteNumber} 입니다.
            </div>
        );
    }
}

MyComponent.defaultProps = {
    name: '기본이름'
};

MyComponent.propTypes = {
    name: PropTypes.string,
    favoriteNumber: PropTypes.number.isRequired
};

export default MyComponent;
// MyComponent.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';

// defaultProps, propTypes는 클래스 내부로 옮길 수 있습니다.
class MyComponent extends Component {
    static defaultProps = {
        name: '기본이름'
    };

    static propTypes = {
        name: PropTypes.string,
        favoriteNumber: PropTypes.number.isRequired
    };

    render() {
        const { name, favoriteNumber, children } = this.props;
        return (
            <div>
                안녕하세요, 제 이름은 {name}입니다.
                <br />
                children 값은 {children} 입니다.
                <br />
                제가 좋아하는 숫자는 {favoriteNumber} 입니다.
            </div>
        );
    }
}

export default MyComponent;

state

  • 컴포넌트 내부에서 바꿀 수 있는 값
  • props는 부모 컴포넌트에서 설정하며, 자식 컴포넌트는 읽기전용으로만 사용가능하다.
// Counter.js
import React, { Component } from 'react';

class Counter extends Component {
    constructor(props) {
        super(props);
        
        // state의 초기값 설정
        this.state = {
            number: 0,
            fixedNumber: 0
        };
    }

    render() {
        const { number,  fixedNumber} = this.state;

        return (
            <div>
                <h1>{number}</h1>
                <h2>바뀌지 않는 값 : {fixedNumber}</h2>
                <button
                    // onClick 이벤트에 수행할 함수를 지정
                    onClick={() => {
                        // setState를 이용해 값을 넣을 수 있음
                        this.setState({ number: number + 1 });
                    }}
                >
                    +1
                </button>
            </div>
        );
    }
}

export default Counter;


// App.js
import React from 'react';
import MyComponent from './MyComponent';
import Counter from './Counter';

const App = () => {
  return <Counter />;
};

export default App;
// Counter.js
import React, { Component } from 'react';

// constructor를 사용하지 않고 state를 초기화 할 수 있다.
class Counter extends Component {
    state = {
        number: 0,
        fixedNumber: 0
    };

    render() {
        const {number, fixedNumber} = this.state;
        return (
            <div>
                <h1>{number}</h1>
                <h2>바뀌지 않는 값 : {fixedNumber}</h2>
                <button
                    // onClick 이벤트에 수행할 함수를 지정
                    onClick={() => {
                        // setState를 이용해 값을 넣을 수 있음
                        this.setState({ number: number + 1 });
                        // setState를 사용해도 state값이 바로 변경되지 않아 값이 +2씩 올라가지 않는다.
                        this.setState({ number: number + 1 });
                    }}
                >
                    +1
                </button>
            </div>
        );
    }
}

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

class Counter extends Component {
    state = {
        number: 0,
        fixedNumber: 0
    };

    render() {
        const { number, fixedNumber } = this.state;
        return (
            <div>
                <h1>{number}</h1>
                <h2>바뀌지 않는 값 : {fixedNumber}</h2>
                <button
                    // 한 번 클릭할 때마다 값이 2씩 증가한다.
                    onClick={() => {
                        // setState를 이용해 값을 넣을 수 있음
                        this.setState(prevState => {
                            return { number: prevState.number + 1 };
                        });
                        
                        // 바로 위 setState와 형태는 다르지만 동일한 기능을 하는 코드
                        this.setState(prevState => ({
                            number: prevState.number + 1
                        }));
                    }}
                >
                    +1
                </button>
            </div>
        );
    }
}

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

class Counter extends Component {
    state = {
        number: 0,
        fixedNumber: 0
    };

    render() {
        const { number, fixedNumber } = this.state;
        return (
            <div>
                <h1>{number}</h1>
                <h2>바뀌지 않는 값 : {fixedNumber}</h2>
                <button
                    // 한 번 클릭할 때마다 값이 1씩 증가한다.
                    onClick={() => {
                        // setState에 콜백함수를 추가할 수 있다.
                        this.setState(
                            {
                                number: number + 1
                            },
                            () => {
                                console.log('콜백함수 호출!!');
                                console.log(this.state);
                            }
                        );
                    }}
                >
                    +1
                </button>
            </div>
        );
    }
}

export default Counter;

함수형 컴포넌트에서 useState 사용하기

// Say.js
import React, {useState} from 'react';

const Say = () => {
    // useState의 배열 첫번째 요소는 현재상태, 두번째 요소는 setter로 구성된다
    const [message, setMessage] = useState('');
    const onClickEnter = () => setMessage('안녕하세요.');
    const onClickLeave = () => setMessage('안녕히 가세요.');

    return (
      <div>
          <button onClick={onClickEnter}>입장</button>
          <button onClick={onClickLeave}>퇴장</button>
          <h1>{message}</h1>
      </div>
    );
}
export default Say;

// App.js
import React from 'react';
import Say from './Say';

const App = () => {
    return <Say />;
};

export default App;
// Say.js
import React, {useState} from 'react';

const Say = () => {
    const [message, setMessage] = useState('');
    const onClickEnter = () => setMessage('안녕하세요.');
    const onClickLeave = () => setMessage('안녕히 가세요.');
    //  state는 여러 번 사용할 수 있다.
    const [color, setColor] = useState('black');

    return (
      <div>
          <button onClick={onClickEnter}>입장</button>
          <button onClick={onClickLeave}>퇴장</button>
          <h1 style={{color}}>{message}</h1>

          <button style={{color:'red'}} onClick={() => setColor('red')}> 빨간색</button>
          <button style={{color:'blue'}} onClick={() => setColor('blue')}> 파란색</button>
          <button style={{color:'green'}} onClick={() => setColor('green')}> 초록색</button>
      </div>
    );
}
export default Say;

state를 사용할 때 주의사항

  • state값을 바꿔야 할 때에는 setState 혹은 useState를 통해 전달받은 세터 함수를 사용해야 된다.
  • 배열이나 객체를 업데이트 할 때는 사본을 만든 후 사본을 업데이트하고, 사본을 setState 또는 세터 함수를 이용해 업데이트 한다.
// 아래처럼 statate 값을 바로 바꿀 수 없다. (아래 코드는 오류발생)
this.state.number = this.state.number + 1;
this.state.array = this.array.push(2);

// 아래와 같이 사본을 이용하여 값을 변경한다.
    const object = {a: 1, b: 2, c: 3 };
    // 사본을 만든 후 b 값을 덮어쓴다.
    // '...' 은 spread 연산자이다. 객체의 사본을 만들 때 이용한다.
    const nextObject = { ...object, b: 4 };     
    console.log(object);  // object: {a: 1, b: 2, c: 3}
    console.log(nextObject);  // nextObject: {a: 1, b: 4, c: 3}

    const array = [
        { id: 1, value: true },
        { id: 2, value: true },
        { id: 3, value: false }
    ];

    let nextArray = array.concat({ id: 4 });
    let array2 = nextArray.filter(item => item.id !== 2);
    let arrat3 = nextArray.map(item => (item.id === 1 ? { ...item, value: false } : item));
    console.log(nextArray);
    // 0: {id: 1, value: true}
    // 1: {id: 2, value: true}
    // 2: {id: 3, value: false}
    // 3: {id: 4}
    console.log(array2);
    /*
    0: {id: 1, value: true}
    1: {id: 3, value: false}
    2: {id: 4}
     */
    console.log(arrat3);
    /*
    0: {id: 1, value: false}
    1: {id: 2, value: true}
    2: {id: 3, value: false}
    3: {id: 4}
     */

props와 state의 차이점

  • props는 부모 컴포넌트가 설정하여 자식 컴포넌트에 전달한다.
  • state는 해당 컴포넌트 자체적으로 가진 값으로 컴포넌트 내부에서 값을 업데이트할 수 있다.
  • 참고 : 리액트에서 데이터의 흐름은 부모-> 자식으로 일방향이다.

You may also like...

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다