[React] 자식 컴포넌트 state 값 부모 컴포넌트로 전달하기 및 불변성 유지

자식 컴포넌트 state 값 부모컴포넌트로 전달하기

방법을 간단히 보면

부모 컴포넌트에 함수를 정의하고 해당 함수를 자식 컴포넌트의 props로 전달한다.

props로 전달된 함수를 호출해서 자식컴포넌트의 값을 부모 컴포넌트로 전달하도록 한다.

App.js (부모 컴포넌트)
import React, { Component, Fragment } from "react";
import Child from "./components/Child";

class App extends Component {
handleData = (data) => {
console.log(data);
};
render() {
return (
<>
<Child sendProps={this.handleData} />
</>
);
}
}

export default App;


Child.js (자식 컴포넌트)
import React, { Component } from "react";

export default class Child extends Component {
state = {
name: "",
age: "",
};

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

handleSubmit = (e) => {
e.preventDefault();
{
this.props.sendProps(this.state);
}
};

render() {
return (
<form onSubmit={this.handleSubmit}>
<input
name="name"
type="text"
onChange={this.handleChange}
value={this.state.name}
/>
<input
name="age"
type="text"
onChange={this.handleChange}
value={this.state.age}
/>
<button>전달하기</button>
</form>
);
}
}


Child 컴포넌트에서 작성된 값을 handleData로 부모컴포넌트로 값을 전달했기 때문에 콘솔에 작성된 값이 출력되는것을 확인할 수 있다.


상위컴포넌트가 하위컴포넌트에게 값을 전달할때는 props를 통해 값을 전달하지만
하위컴포넌트가 상위컴포넌트에게 값을 전달할때는 이벤트를 통해 값을 전달한다.

일반적인 HTML에서 요소안에 작성하는 이벤트와 다르게 React에서 이벤트 작성시에는 카멜케이스(camelCase) 형태로 작성해야한다. 

하위 컴포넌트 내부에 작성되어있는 요소(element)에 이벤트를 작성한다. 이벤트가 발생되면 값을 props로 전달된 함수에 인자값으로 전달하여 하위컴포넌트는 상위컴포넌트로 값을 전달하여 상위컴포넌트의 기존의 state의 값을 수정할 수 있다.

한가지 주의할점은 class field에 함수를 정의하지않고 요소(태그)에 직접 함수를 정의하는 경우이다. 이 방식은 값의 변경으로 새롭게 렌더링이 될때마다 새로운 콜백함수가 생성되는 문제가 발생한다. 

대부분의 경우에는 상관이 없지만 태그안에 직접 정의되어있는 콜백함수가 하위 컴포넌트의 props로 넘어가게 되면 하위컴포넌트에서 추가적인 렌더링이 발생하게 된다. 그래서 성능문제를 피하기 위해서 태그안에 이벤트에 함수를 직접 정의하지않고 binding이나 Arrow Function 또는  class field syntax를 사용하는 것을 권장한다.



불변성 유지

App.js
import React, { Component, Fragment } from "react";
import PhoneForm from "./components/PhoneForm";

class App extends Component {
id = 0;

state = {
information: [],
};
handleData = (data) => {
this.setState({
information: this.state.information.concat({
...data,
id: this.id++,
}),
});
};
render() {
return (
<>
<PhoneForm onCreate={this.handleData} />
{JSON.stringify(this.state.information)}
</>
);
}
}

export default App;



PhoneForm.js
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() {
console.log(this.state.name);
console.log(this.state.phone);
return (
<form onSubmit={this.handleSubmit}>
<input
name="name"
type="text"
onChange={this.handleChange}
value={this.state.name}
/>
<input
name="phone"
type="text"
onChange={this.handleChange}
value={this.state.phone}
/>
<button>정보 등록</button>
<div> 이름:{this.state.name}</div>
<div> 번호:{this.state.phone}</div>
</form>
);
}
}

export default PhoneForm;


자식 컴포넌트에서 부모 컴포넌트에 값을 전달해서 부모 컴포넌트의 state 값을 변경할 수 있다.

그런데 이때 부모컴포넌트의 state 값이 배열인 경우 주의해야할 점이 있다.

react의 경우 이전에 생성된 컴포넌트와 값이 변경된 컴포넌트를 비교해서 변경된 부분만 리렌더링을 시켜준다.

그런데 이때 값의 불변성을 유지 시켜주지 않으면 이전의 값과 새롭게 업데이트된 값의 비교가 불가능해져서 제대로 업데이트를 못하는 문제가 발생된다.

그러므로 기존의 배열은 그대로 두고 기존의 배열을 복제하고 값을 추가하여 새로운 배열을 반환하는 concat을 사용해서 불변성을 유지할 수 있다.

이렇게되면 이전의 배열과 비교하여 업데이트된 부분만 새롭게 리랜더링 시켜주어 정상적으로 동작하게 된다.



댓글