React
main docs
- 포스팅 설명이 부족하다면 검색 하기 전에 아래 링크에서 추가적인 정보를 찾아볼 것. react 공식 docs가 워낙 잘 되어 있어서, 이 것만 다 읽어도 웬만한 상황에서는 충분하다.
- github.com/umbum/react-basic-hooks/blob/master/docs/README.md
- https://ko.reactjs.org/docs/hello-world.html
심화 문서
- 기존의 “class 기반 컴포넌트” 대비 “함수형 컴포넌트(with hooks)”의 장점은 무엇인가? https://ko.reactjs.org/docs/hooks-intro.html
- useState 같은 hook은 함수형 컴포넌트의 등장에 따라 필수적으로 생길 수 밖에 없었다.
- React는 어떻게 바뀐 항목만 re-render 할 수 있는가? https://ko.reactjs.org/docs/reconciliation.html
React component
- props를 전달 받아 React Element를 반환하는 function(함수형 컴포넌트) 혹은 class(클래스형 컴포넌트)
- props는 react component의 파라미터, state는 react component의 필드라고 생각하면 됨.
- state라는 내부 상태를 가질 수 있음
- 클래스형 컴포넌트는 필드로 state 가짐 (
this.state
) - 함수형 컴포넌트는 ‘클래스가 아니라 함수‘라서, 필드가 아닌 함수가 끝나면 사라지는 로컬 변수밖에 가질 수 없음. 함수가 끝나도 사라지지 않는 state를 가지기 위한 다른 방법이 필요함. ⇒
useState()
hooksconst [count, setCount] = useState(0);
state 변수와 업데이트 함수를 반환.- https://ko.reactjs.org/docs/hooks-state.html
- 클래스형 컴포넌트는 필드로 state 가짐 (
- 컴포넌트가 re-render 되는 시점은?
- On mount / props 변경 / state 변경 / context 변경
- 변경되었는지 검사는 Object.js()로 함.
- 동일 state로 변경되는 경우 re-render 하지 않지만, 컴포넌트 내부의 객체나 배열 레퍼런스가 달라지면 다른 state로 인식
MobX를 사용하는 경우 setState를 안써도 된다. observer가 자동으로 추적하기 때문.
React state 관리 (Class Component 인 경우)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ClassComponent extends Component {
// state 초기화는 이렇게
state = {
s1: true,
s2: {
s2a: 0,
s2b: 1
}
};
actions = {
changeS2a: (value) => {
// state 변경은 이렇게
this.setState(prevState => {s2: Object.assign(prevState.s2, {s2a: value})});
/\* s2 전체가 대체되기 때문에 Object.assign(또는 spread) 사용하여 s2b는 보존해야 함. \*/
},
}
}
- https://ko.reactjs.org/docs/react-component.html
- https://ko.reactjs.org/docs/faq-state.html
this.state
를 직접 변경하면 안 됩니다. 왜냐하면 이후 호출되는setState()
가 이전에 적용된 변경 사항을 덮어쓰기 때문입니다.this.state
를 불변적(Immutable)인 데이터로 취급하세요.- 항상 setState 가 가장 최신의 state 값을 사용하도록 보장하기 위해서는 setState 에 객체 대신 함수를 전달 하세요.
- X -
this.setState({})
- O -
this.setState(prevState => {})
- X -
setState()
는 비동기로 동작한다.
React state 관리 (Functional Component 인 경우 + hooks)
1
2
3
4
5
6
7
export default function useFunctionalComponent() {
const [s1, setS1] = useState(true);
const [s2, setS2] = useState({ s2a: 0, s2b: 1 });
const changeS2a = (value) => {
setS2({ ...s2, ...{ s2a: value } });
}
}
- https://ko.reactjs.org/docs/hooks-state.html
- 함수형 컴포넌트에서, state가 변경되며 컴포넌트가 re-render 될 때, 컴포넌트 내부가 다시 실행되면서 함수나 변수가 다시 생성된다.
hooks
https://ko.reactjs.org/docs/hooks-reference.html 참고하면 된다. 상세 내용을 비롯한 다양한 hooks.
- useState
- useEffect
- componentDidMount 와 componentDidUpdate , componentWillUnmount 가 합쳐진 것으로 생각해도 좋다.
- useContext
- 클라이언트 컴포넌트에서 대상 context를 사용(구독)하겠다 선언.
- 위 링크와, 아래 React Context 부분 참조
- useCallback
- state 변경 - re-render 될 때 컴포넌트 내부에서 함수가 재생성 될 필요가 없을 때(또는 재생성 되지 않아야만 할 때) 감싸준다.
- e.g., 함수 재생성으로 debounce 등이 제대로 동작하지 않는 경우
- useMemo
- useCallback과 비슷하게 컴포넌트 내부에서 변수가 재생성 될 필요가 없을 때. useCallback은 함수용, useMemo는 변수용.
React Context
Context가 왜 필요한가?
- React에서 데이터는 props를 통해 위에서 아래로(parent에서 child로) 전달된다.
- props는 react component의 파라미터라고 생각하면 됨.
- 그러나 이렇게 top-down 방식으로 데이터를 전파하는 것은 앱이 약간만 규모가 커져도 전달해야 하는 props가 많아진다. 쓸데없는 props를 단지 전달 목적으로 받는 경우도 있을거고… 전달… 전달… 전달… 이다.
- 그래서 context라는 개념이 도입되었고, context를 이용하면 컴포넌트 트리 전체에 데이터를 제공할 수 있다.
- 예를들면 로케일, UI 테마 등. 앱 안의 여러 컴포넌트에게 전달해줘야 하는 데이터인 경우.
- Context API는 어떤 상태가 변경이 되었을 때 그 컨텍스트를 구독하는 모든 컴포넌트가 리렌더링 된다는 점을 생각해야 한다.
- 다른 페이지에서 상태를 변경했는데… 그 변경한 상태가 또 다른 페이지에 영향을 미칠 수 있다.
Context, Provider의 관계?
1
2
3
4
5
6
7
8
9
10
.... context 선언부 ....
const MyContext = createContext();
// const { Provider, Consumer } = MyContext;
const useMyContext = () => {
return useContext(MyContext);
};
.... MyContext가 변경되었을 때 리렌더링 되어야 하는 클라이언트 코드에서 아래 호출 ....
const { myVariable, setMyVariable } = useMyContext();
useMyContext()
를 호출한 클라이언트는useContext(MyContext)
를 호출하는 것과 같다.useContext(MyContext)
를 호출한 클라이언트 컴포넌트는,MyContext
에 변경이 생길 때 마다 리렌더링 된다.
헌데 useContext(MyContext)
가 { myVariable }
을 반환하는데, myVariable
에 어떤 값이 들어가는지에 대한 정보가 없다?
- context의 현재 값은 트리 안에서 이 Hook을 호출하는 컴포넌트에 가장 가까이에 있는
<MyContext.Provider>
의 value prop에 의해 결정됩니다.- Context 오브젝트에 포함된 React 컴포넌트인 Provider는 context를 구독하는 컴포넌트들에게 context의 변화를 알리는 역할을 합니다.
- Provider 컴포넌트는 value prop을 받아서 이 값을 하위에 있는 컴포넌트에게 전달합니다.
- Provider 하위에서 context를 구독하는 모든 컴포넌트는 Provider의 value prop가 바뀔 때마다 다시 렌더링 됩니다.
- 즉,
<MyContext.Provider value=...>
를 이용해서, context의 실체를 주입해주어야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const MyContext = createContext(null);
export function useMyContext() {
return useContext(myContext);
}
export function MyProvider({ children }) {
const [state, setState] = useState({
s1: ...,
s2: {...}
});
const actions = useMemo(() => ({
func1() {},
func2() {}
}), [state])
const value = useMemo(() => [state, actions], [state, actions]);
return <MyContext.Provider value={value}>{children}</MyContext.Provider>
}
useMemo
쓴 이유? 물론 이로 인한 성능 개선은 미미 할 수 있다.
actions에서 state를 참조하고 있다면(99%가 그렇다), state가 변경되었을 때 actions가 다시 계산되어야 하므로 반드시 의존성 배열에 state를 넣어주어야 함수가 의도한 동작대로 실행 될 수 있다.
When a component that is a descendant of a provider is rendered, the provider is also rendered. Providers are typically rendered when the application initially mounts and whenever there is a re-render triggered by a state change, prop change, or forceUpdate.
React’s
useState
hook is called during the initial render of a component, and it returns a stateful value along with a function to update that state. The crucial aspect ofuseState
is that it preserves the state between renders, meaning that the state is retained even if the component re-renders. IfMyProvider
is re-rendered (due to changes in state, props, or context), the same state variable and updater function are used. React keeps track of the state internally, so it doesn’t lose the previous state between renders.
Radio input을 Context 관리하의 Component로 만드는 예제
https://www.daleseo.com/react-radio-buttons/
기타
react에서는 DOM element에 사용하는 키워드를 바꿔줘야 함
https://ko.reactjs.org/docs/dom-elements.html
for -> htmlFor / class -> className
으로 변경되는 등.