↔ 양방향 바인딩?
양방향 데이터 바인딩에서는 부모 컴포넌트와 자식 컴포넌트 간에 데이터가 서로 양방향으로 흐른다.
부모와 자식 모두 데이터를 직접 변경할 수 있다.
model(JavaScript)과 view(HTML)가 있을 때, 양방향 데이터 바인딩은 model이 업데이트 되면 view가 업데이트 되고, 반대로 view가 업데이트되면 model이 업데이트 되는 것이다.
이를 자동 동기화라고도 한다. 상호 구독 관계라고 생각하면 이해하기 쉽다.
하지만 변화 가능성이 열려있다 보니 데이터 흐름을 예상하기 어렵다는 단점이 있다.
이같은 단점의 대표적인 사례로 페이스북의 알림 버그가 있다.
👇 리액트는 단방향 바인딩
리액트에서 일반적으로 state 관리를 할 때는 props를 통해 부모에서 자식 컴포넌트로의 단방향(하향식) 데이터 흐름이 일어난다. 이로 인해 데이터의 흐름을 명확히 알 수 있고 예측 가능하다.
그렇다면 자식에서는 부모 컴포넌트의 state를 변화시킬 수 없을까?
물론 바꿀 수 있다.
리액트는 양방향 바인딩을 지원하지 않지만, 예를 들어 자식 컴포넌트에서 event (onChange, onClick, onBlur, …)가 발생할 때 부모 컴포넌트에서 전달한 핸들러를 통해 state를 바꾸는 등의 양방향 바인딩 같은 동작이 일어나도록 하면 된다. 이를 state 끌어올리기(lifting state up)를 통해 구현할 수 있다.
https://ko.reactjs.org/docs/lifting-state-up.html
💡 부모 컴포넌트의 state를 바꾸는 방법
간단한 예시를 통해 설명하겠다.
부모 컴포넌트는 자식 컴포넌트 1과 자식 컴포넌트 2를 가지고 있다.
자식 컴포넌트 2에서는 리스트를 렌더링하고 있고, 자식 컴포넌트 1에서 리스트에 변화를 주어야 한다.
React에서 state를 공유하려면 그 데이터를 필요로 하는 컴포넌트 간의 가장 가까운 부모로 state를 끌어올리면 된다. 즉, 자식 컴포넌트 2에서 state를 가지고 있었다면 그걸 부모 컴포넌트가 가진 뒤 props로 내려주는 것이다.
import { useState } from 'react';
import First from './First';
import Second from './Second';
function App() {
const [list, setList] = useState(['리스트1']);
const addList = (value) => {
setList([...list, value]);
};
return (
<>
<h1>부모 컴포넌트</h1>
<First addList={addList} />
<Second list={list} />
</>
);
}
export default App;
이제 부모 컴포넌트가 초깃값이 ['리스트1']
인 list state를 가지고 자식 컴포넌트 2로 내려주게 되었다.
그렇다면 자식 컴포넌트 1은 어떻게 부모 컴포넌트의 상태를 바꿀 수 있을까?
마찬가지로 부모 컴포넌트에서 상태변화 함수를 만들어 자식 컴포넌트 1로 내려주면 된다.
addList라는 함수에서 기존 list 배열에 매개변수로 받은 value를 추가할 수 있게 setList로 state를 변화시킨다.
import { useRef } from 'react';
const First = ({ addList }) => {
const text = useRef(null);
const onClickSubmit = (e) => {
e.preventDefault();
addList(text.current.value);
text.current.value = '';
};
return (
<>
<h2>자식 컴포넌트 1</h2>
<form>
<input type="text" ref={text} />
<button onClick={onClickSubmit}>추가</button>
</form>
</>
);
};
export default First;
이제 자식 컴포넌트 1로 와서 onClickSubmit 함수를 만들어 주었다.
폼이 submit 됐을 때의 기본 동작을 막기 위해 e.preventDefault()
를 한 다음,
props로 내려받은 addList 함수의 인자로 input에 입력한 값을 넣어 주면 부모 컴포넌트의 state를 변화시킬 수 있다. state가 변화한 다음 input 값을 비워주기 위해 text.current.value
를 빈 문자열''
로 만들어 주었다.
그리고 button의 온클릭 이벤트로 onClickSubmit을 전달한다. 이때 ( )
를 붙이지 않는다.
이렇게 하면 의도대로 동작한다.
이런 간단한 예시에서는 괜찮지만 만약 props drilling이 너무 깊어진다면
Context API나 상태 관리 라이브러리를 사용하는 것이 좋다.
🔎 앵귤러와 뷰
React와 마찬가지로 단방향 바인딩만을 지원하는 Angular와 Vue에서
Angular는 [(ngModel)]
, Vue는 v-model
을 사용해 양방향 데이터 바인딩처럼 동작하게 할 수 있다.
🟣 리덕스
Redux 또한 단방향 데이터 흐름을 사용한다.
store에서 모든 state를 관리하고 있고, action을 dispatch해야 reducer가 state를 업데이트할 수 있다.
그 후 해당 state를 구독하는 컴포넌트들이 선택적으로 리렌더링 된다.
Reference>
- https://sandroroth.com/blog/react-two-way-data-binding
- https://velog.io/@kimju0913/단방향-바인딩과-양방향-바인딩
- https://velopert.com/1225
- https://ui.toast.com/weekly-pick/ko_20151027