생활코딩 React #5

Create 기능 구현

props 와 state 의 차이점

  • props 는 readOnly 이다. 그래서 수정이 불가능하다.
  • state 는 setState() 를 통해서 변경이 가능하다.
<Content newProps={_someProps}></Content>
//이런 식으로 props를 넣어주는 쪽에서는 props를 변경할 수 있다

class Content extends Component {
	...
	render() {
		this.props.newProps = 'hi';	//Error
		//이런 식으로 props를 받는 입장에서는 props를 수정할 수 없다.
	}
	...
}

상위 컴포넌트인 App 클래스가 있다고 하고 그 안에 Content라는 하위 컴포넌트가 있다고 하면 App 클래스 내부에서 <Content></Content> 형식으로 사용을 할 것이다. 이 때 상위 컴포넌트인 App은 하위 컴포넌트인 Content에 props로 값을 전달한다. 반면에 하위 컴포넌트인 Content에서 상위 컴포넌트인 App에 값을 변경할 때는 이벤트를 이용해서 setState() 함수로 값을 변경해준다.

create 기능 구현하기

  • create 를 할 수 있는 버튼을 만든다.
  • 새로 추가할 컴포넌트를 구현한다.
  • 버튼을 누르면 새로운 컴포넌트를 추가하도록 이벤트를 등록한다.
if (this.state.mode === "create") {
  _article = (
		<CreateContent	//새로 추가할 컴포넌트
		//onSubmit은 기존에 리액트나 html에 정의돼있는 함수가 아니라
		//내가 직접 이름지은 props 이다.
    	onSubmit={function (_title, _desc) {
      	this.max_content_id = this.max_content_id + 1;
      	const _contents = this.state.contents.concat({
        	id: this.max_content_id,
        	title: _title,
        	desc: _desc,
      	});
      	this.setState({
      		contents: _contents,
      	});
			}.bind(this)}>
		</CreateContent>
  );

위 코드와 같이 새로운 컴포넌트를 구현해 놓고 그 컴포넌트의 submit 버튼을 누르면 onSubmit 이벤트가 실행되도록 함수를 정의한다. 이 때 이 함수에서 this.setState 함수를 사용해서 값을 변경한다.

class CreateContent extends Component {
  render() {
    return (
      <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)}
        >
				...
    );
  }
}

onSubmit 이벤트가 발생하면 이 컴포넌트 안에서 정의한 함수가 실행되는데 이 때 props로 넘어온 onSubmit 함수를 실행시켜주고 해당 값으로 적절한 값을 (이 경우에는 target의 value값들) 넣어준다.

shouldComponentUpdate

state의 값이 바뀔 때마다 모든 컴포넌트의 render() 함수가 실행되는데 전혀 실행될 필요가 없는 state값이 바뀔때도 실행이 되니까 프로그램이 매우 크다면 성능상의 문제가 될 수 있다.

그래서 리액트에서는 그런 부분에 대해서 개발자가 직접 결정할 수 있도록 shouldComponentUpdate 라는 함수를 정의해놨다.

이 함수에는 두 개의 매개변수를 받을 수 있는데 첫 번째 매개변수는 newProps 이고 두 번째는 newState 이다.

그리고 return 값으로 boolean 타입을 갖는데 true를 리턴하면 해당 클래스의 render() 함수가 실행이 되고 false 를 반환하면 render() 함수가 실행되지 않는다. 이에 따라서 관련 있는, 꼭 렌더링을 다시 해야 하는 경우만 true를 return 하게끔 해서 불필요한 렌더링을 막을 수 있는 것이다.

그런데 이 부분에서 유의해야할 이슈가 하나 있다.
리액트에서는 state의 값을 바꿀 때 setState 함수를 사용해서 객체 형태로 인자를 넣어줘서 변경시켜준다고 했다. 그런데 이 함수를 활용할 때 예를 들어서 어떤 배열의 값에 push함수를 사용한다면 push 함수 특성상 원본 함수 자체를 변경해버린다. 그렇게 되면

this.state.contents.push({
  id: this.max_content_id,
  title: _title,
  desc: _desc,
});
setState({ contents: this.state.contents });

이런 식으로 contents 배열의 내용을 바꾸게 될텐데 원본 배열을 수정해버리니까 shouldComponentUpdate 함수에서 newProps 와 그 이전의 props 값 비교가 불가능하게 된다. console.log 로 찍어보면 shouldComponentUpdate 함수 내부에서

console.log(newProps.data);
console.log(this.props.data);

를 실행했을 때 원본 자체를 push 함수로 바꿨기 때문에 두 개의 결과값이 똑같이 나오게 된다. 그렇다면 우리가 의도한 props 값이 변경된 것을 알 수가 없기 때문에 적절하게 이 함수를 사용할 수가 없게 된다. 그런 이유로 push를 하지 않고 새로운 배열을 return 하는 concat() 함수를 사용해서 state 값을 변경해줘야 한다.

const newContents = this.state.contents.concat({
  id: this.max_content_id,
  title: _title,
  desc: _desc,
});
setState({ contents: newContents });

//push를 쓰고 싶다면 복제 이후에 하면 된다.
const newContents = Array.from(this.state.contents);
newContents.push({
	id: this.max_content_id,
  title: _title,
  desc: _desc,
});
setState({ contents: newContents });
//이렇게 한 경우 push를 썻지만 그 때는 변경된 값이 복제된 
//newContents 이기 때문에 문제가 되지 않는다.

결론적으로 위 코드처럼 concat함수를 사용하여 원본 함수를 수정하지 않고
새로운 변수에 복제된 값을 setState 함수 내부에서 넣어줘야지 리액트는 기존의 변경되기 전 값과 변경 후의 값을 구분할 수 있게된다.
그래야만 shouldComponentUpdate 함수를 의도한대로 활용할 수 있다.

좋은 웹페이지 즐겨찾기