컴포넌트 사이에서 상태 로직을 재사용하기 어렵다.
React는 컴포넌트 간에 재사용 가능한 로직을 붙이는 방법을 제공하지 않는다.
이를 강제하기 위해 render props
나 고차 컴포넌트
같은 패턴을 통해 이를 해결했다.
//render props
<DataProvider render={data => (
<h1>Hello {data.target}</h1>
)}/>
// 고차 컴포넌트(HOC, Higher Order Component)
// 컴포넌트를 가져와 새 컴포넌트를 반환하는 함수입니다.
const EnhancedComponent = higherOrderComponent(WrappedComponent);
하지만 이런 방법은 컴포넌트의 재구성을 강요하며, 코드의 추적을 어렵게 만든다.
providers, consumers, 고차 컴포넌트, render props 그리고 다른 추상화에 대한 레이어로 둘러싸인 “래퍼 지옥(wrapper hell)“을 볼 가능성이 높다.
복잡한 컴포넌트의 이해가 어렵다.
componentDidMount
와 componentDidUpdate
는 컴포넌트안에서 데이터를 가져오는 작업을 수행할 때 사용 되어야 하지만, 같은 componentDidMount
에서 이벤트 리스너를 설정하는 것과 같은 관계없는 로직이 포함되기도 하며, componentWillUnmount
에서 cleanup 로직을 수행하기도 한다.class FriendStatusWithCounter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0, isOnline: null };
this.handleStatusChange = this.handleStatusChange.bind(this);
}
componentDidMount() {
document.title = `You clicked ${this.state.count} times`;
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentDidUpdate() {
document.title = `You clicked ${this.state.count} times`;
}
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
handleStatusChange(status) {
this.setState({
isOnline: status.isOnline
});
}
// ...
document.title
을 설정하는 로직이 componentDidMount
와 componentDidUpdate
에 나누어져 있습니다. 구독(subscription)로직 또한 componentDidMount
와 componentWillUnmount
에 나누어져 있네요. componentDidMount
가 두 가지의 작업을 위한 코드를 모두 가지고 있습니다.function FriendStatusWithCounter(props) {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
// ...
}
Class는 사람과 기계를 혼동시킨다.
this
키워드가 어떻게 작동하는지 알아야만 한다. JavaScript의 this
키워드는 대부분의 다른 언어에서와는 다르게 작동함으로 사용자에게 큰 혼란을 주었으며, 코드의 재사용성과 구성을 매우 어렵게 만들고는 했다.기본적으로 Hooks는 UI의 상태 관련 동작 및 부수 작용(side effects)을 캡슐화하는 가장 간단한 방법이다.
useState 구현하기
const React = (function () {
let hooks = [];
let idx = 0; //여러 hook이 사용될 것을 고려함.
function useState(initialVal) {
const state = hooks[idx] || initialVal;
const _idx = idx;
const setState = (newVal) => {
hooks[_idx] = newVal;
};
idx++;
return [state, setState];
}
return { useState };
})();
function Component() {
const [count, setCount] = React.useState(1);
const [text, setText] = React.useState("apple");
return {
render: () => console.log({ count, text }),
click: () => setCount(count + 1),
type: (word) => setText(word),
};
}
var App = React.render(Component); // { count: 1, text: 'apple' }
App.click();
var App = React.render(Component); // { count: 2, text: 'apple' } 😀
App.click();
var App = React.render(Component); // { count: 3, text: 'apple' } 😀
App.type("orange");
var App = React.render(Component); // { count: 3, text: 'orange' } 😀
App.type("peach");
var App = React.render(Component); // { count: 3, text: 'peach' } 😀
useEffect 구현하기
const React = (function () {
let hooks = [];
let idx = 0;
function useState(initialValue) {
//...
}
function useEffect(cb, depArray) {
const oldDeps = hooks[idx];
let hasChanged = true; // default
if (oldDeps) {
hasChanged = depArray.some((dep, i) => !Object.is(dep, oldDeps[i])); //첫번째 인자와 두번째 인자가 같은지를 판정하는 메서드
}
// 변경을 감지
if (hasChanged) {
cb();
}
hooks[idx] = depArray;
idx++;
}
return { useState, useEffect };
})();
구현 결론
<aside> 🚧 개인의 의견으로만 받아들이기.
</aside>