useState를 깊게 알아보자
State Hook을 자체적으로 다시 구현하여 내부적으로 어떻게 작동하는지 배우는 것으로 시작하겠습니다. 다음으로 Hook의 몇 가지 제한 사항과 Hook이 존재하는 이유에 대해 알아봅니다. 마지막으로 Hooks의 한계로 인해 발생하는 일반적인 문제를 해결하는 방법을 배웁니다. 최종적으로 React에서 stateful 함수 컴포넌트를 구현하기 위해 State Hook을 사용하는 방법을 알게 될 수 있도록 하겠습니다.
useState 구현
Hooks가 내부적으로 어떻게 작동하는지 더 잘 이해하기 위해 Hooks를 다시 구현해보겠습니다.
지금부터 작성하는 코드는 실제 useState를 구현하는게 아니라 개념과 아이디어를 파악하는데 이해를 위한 코드라는 것을 기억해주세요.
import React from 'react'
import ReactDOM from 'react-dom'
function Temp () {
// .........
function useState (initialState) {
let value = initialState
function setState (nextValue) {
value = nextValue
ReactDOM.render(<MyName />,
document.getElementById('root'))
}
return [ value, setState ]
}
// .........
}
코드에 대해서 간략히 설명해보자면,
먼저, ReactDOM은 Component를 재랜더링하기 위해 필요합니다.
반환값이 객체가 아닌 배열을 사용되는 이유는 일반적으로 value & setState 을 rename하여 사용하기 때문입니다.
const [name, setName] = useState('')
정의한 Hook 함수를 보면 값을 저장하기 위해 closure를 사용하고 있습니다.
setState함수는 동일한 closuer 내에서 정의되어 있으며, 이것이 우리가 함수 컴포넌트 내에서 우리가 값에 접근할 수 있는 이유입니다. 또한, useState없이는 해당 값을 직접 접근할 수 없으며 변수 값을 반환받지도 않습니다.
위 코드의 문제점
1. Global Variable
Hook을 실행하게 되면, 컴포넌트가 다시 렌더링될 때 상태가 재설정되어 초기화됩니다. 우리는 렌더링 될때마다 useState를 호출하기 때문에 값이 새로 초기화 되기 때문입니다.
우리는 이것을 global 변수로 해결할 수 있습니다.
value는 useState에 의해 정의된 클로져안에 저장되어있습니다. 컴포넌트가 re-rendering될 때마다, 그 클로저는 다시 초기화됩니다. 그것은 값의 리셋을 의미합니다. 이것을 해결하기 위해, 우리는 값을 함수 외부의 global variable에 저장할 필요가 있습니다.
그 방법은, value는 함수의 밖 클로저에 존재 하게 해주어 랜더링을 다시 해도 초기화가 되지 않도록 해줍니다.
useState 구현과 별 다른 것이 없다. useState밖에 변수를 정의하고 내부 함수에 조건을 조금 추가해주면 끝이다.
let value
function useState (initialState) {
if (typeof value === 'undefined') value = initialState
이제부터는 우리의 useState 는 전역값 변수를 정의하는 대신 값 변수가 외부 클로저에 있으므로 함수가 다시 호출 되어도 다시 초기화되지 않습니다.
2. 여러 hook을 사용하는 방법
우리는 한 컴포넌트에 여러 hook을 사용해야 하는 경우가 분명히 있습니다. 하지만 위에서 구현한 것은 value 변수 하나만을 이용하기에 여러 hook을 사용하기 어렵습니다. 그럼 어떻게 해야할까 고민하면 답은 [] (배열)입니다.
리팩토링을 해보겠습니다.
values[currentHook++]을 사용하여, values배열의 index로 현재 value를 전달 받을 수 있게 되었습니다.
여태까지 useState의 아이디어를 통한 간단한 구현을 하였습니다. 아이디어 개념과 hook을 이해하기 위해 구현했을 뿐 실제 useState와 다르다는 것을 알아주세요.
그럼 이제 여태까지 배운 hook을 통해 알 수 있는 것을 정리 해보겠습니다.
1. hook에는 clouser를 사용했기 때문에 컴포넌트 안 어디서든 사용이 가능하다.
2. Hook을 정의할 때, 배열을 이용하게 되므로 Index가 들어간다.
Conditional Hook는 정의 가능한가?
위에서 Hook을 정의할 때 인덱스가 들어간다는 것을 배웠습니다. 근데 만약에 조건에 따라 useState가 정의되고 안되고 차이가 있다면, Hook의 index는 순서가 엉망이 될 수 밖에 없습니다. 결국 조건에 따라 useState를 사용하면 안된다는 것이죠
const [ enableFirstName, setEnableFirstName ] = useState(false)
const [ name, setName ] = enableFirstName ? useState(''): [ '', () => {} ]
실제 Hook 비교
Hook을 구현을 해보았지만 이것은 내부적으로 어떻게 동작하는지에 대한 아이디어를 제공할 뿐 실제와는 다릅니다.
먼저, Hooks는 global variable을 사용하지 않습니다. 대신 React Component안에 state를 저장합니다. 또한 내부적으로 Hook카운터를 처리하므로 컴포넌트 내에서 수동으로 재설정할 필요도 없습니다.
두번째, 실제 Hook은 상태가 변경될 때 구성 요소의 재렌더링을 자동으로 트리거 합니다. 그러나 이를 수행하기 위해서는 React 함수 컴포넌트 내에서 Hook을 호출해야 합니다. React Hooks는 React 외부 또는 클래스 컴포넌트에서 사용할 수 없습니다.
결론적으로 useState를 재구현해보면서 우리는 3가지를 배울 수 있었습니다.
- Hooks는 단순히 React기능에 접근하는 기능입니다.
- Hooks의 정의 순서는 중요하다
- Hooks는 재랜더링에 지속되는 side Effect를 처리한다.
특히, 정의 순서는 Hooks를 조건부로 정의 할 수 없음을 의미하기 때문에 중요합니다.
Hook의 일반적인 문제 해결
일반적인 Hooks를 구현하는 것은 장단점이 있습니다. 이제 React Hooks의 한계에서 비롯된 이러한 문제들을 극복하는 방법들을 알아 보겠습니다.
- 조건 hooks 해결
- 루프에서 hooks
1. 조건 Hooks
우리의 컴포넌트를 분할해서 프로젝트를 진행합니다. 따라서 항상 정의하고 필요에 따라 컴포넌트를 불러 Hooks를 사용하게 합니다.
function LoggedInUserInfo ({ username }) {
const info = useFetchUserInfo(username)
return <div>{info}</div>
}
function UserInfo ({ username }) {
if (username) {
return <LoggedInUserInfo username={username} />
}
return <div>Not logged in</div>
}
위 코드에 대해서 설명하자면, 로그인 상태와 아닌 상태에 대해 개별 Component를 사용하였습니다.
이를 통해 조건부 Hook 없는 것이 문제가 되지 않습니다.
2. Hooks in Loop
우리는 간단하게 배열을 통해 해결할 수 있습니다.
function OnlineUsers ({ users }) {
const [ userInfos, setUserInfos ] = useState([])
// ... fetch & keep userInfos up to date ...
return (
<div>
{users.map(username => {
const user = userInfos.find(u => u.username === username)
return <UserInfo {...user} />
})}
</div> )
}
문단 마지막 사용
useState을 global state 와 closures를 통해 재구성해보았습니다. 그런 다음 여러 Hook을 구현하려면 대신 state 배열을 사용해야 한다는 것을 배웠습니다. 그러나 state 배열을 사용함으로써 우리는 함수 호출에서 Hook 의 순서를 일관되게 유지해야 했습니다. 이 제한으로 인해 루프에서 조건부 Hooks 및 Hooks가 불가능했습니다. 마지막으로, Hooks의 한계에서 비롯된 일반적인 문제를 해결하는 방법을 배웠습니다.
이제 Hooks의 내부 작동 과 한계에 대해 확실히 이해할 수 있을 것입니다.
'Front-end > React' 카테고리의 다른 글
React Hook의 모든 것 (1) (0) | 2022.02.21 |
---|---|
React 프로젝트 설정 및 확장성 이야기 (0) | 2022.02.15 |
React Rendering optimization (최적화) (0) | 2022.02.10 |
React 랜더링 기본 (0) | 2022.02.10 |