useReducer
- 컴포넌트 상태값을 리덕스의 리듀서처럼 관리
- useState 를 사용해도 좋으나, 개인적으로 깊이가 있는 자식 컴포넌트에게 상태값 변경을 하는 함수를 전달 시, useReducer 로 상태를 관리하면서 dispatch 로 통일화 시키면 좋은거 같다는 생각을 했다.
import React, {useReducer, useState, useRef} from 'react';
import ChildComponent from "./components/ChildComponent";
import PublicChildComponent from "./components/PublicChildComponent";
import UseLayoutEffectComponent from "./components/UseLayoutEffectComponent";
const INITIAL_STATE = {
name: 'default',
age: 0
};
function reducer(state, action) {
switch (action.type) {
case 'SET_NAME' :
return {
...state,
name: action.name
};
case 'SET_AGE' :
return {
...state,
age: action.age
};
default :
return {
...state
};
}
}
const SET_NAME = 'SET_NAME';
const SET_AGE = 'SET_AGE';
// context API 생성
export const AppContext = React.createContext(null);
export default function App() {
const [state, dispatch] = useReducer(reducer, INITIAL_STATE);
const publicChildComponentRef = useRef();
return (
<div style={{border: '1px dotted pink', padding: '4px'}}>
<p>
useReducer
</p>
<p>
my name is {state.name}
</p>
<p>
my age is {state.age}
</p>
<div>
<button onClick={()=>dispatch({type : SET_NAME, name : '홍길동'})}>이름 변경</button>
<button onClick={()=>dispatch({type : SET_AGE, age : 20})}>나이 변경</button>
</div>
<hr/>
<div>
<p>
context API 와 useReducer 훅을 이용하면, 상위 컴포넌트에서 깊은 하위컴포넌트에게 이벤트 처리 함수를 쉽게 전달이 가능<br/>
dispatch 함수는 값이 변하지 않는 특징이 있음(useCallback 훅을 이용하여 기억할 필요도 없음)
</p>
<AppContext.Provider value={dispatch}>
<ChildComponent/>
</AppContext.Provider>
</div>
<div style={{border : '1px dotted red', padding : '4px'}}>
<p>
useImperativeHandle
</p>
<PublicChildComponent publicChildComponentRef={publicChildComponentRef}/>
<button onClick={() => publicChildComponentRef.current && publicChildComponentRef.current.addNumberValue()}>숫자 값 증가시키기</button>
<button onClick={() => publicChildComponentRef.current && publicChildComponentRef.current.setNumberValue(0)}>숫자 값 초기화</button>
</div>
<div style={{border : '1px dotted blue', padding : '4px'}}>
<UseLayoutEffectComponent/>
</div>
</div>
);
}
- useReducr 훅은 첫번째 인자는 reducer 함수이며, 두번째 인자는 초기 상태값
- 리덕스의 dispach 함수와 같은 방식으로 사용하여 state 를 업데이트 해준다.
- context API 와 userReducer 훅을 잘 이용하면, 이벤트 처리 함수를 깊은 자식 컴포넌트에게 쉽게 전달이 가능(dispatch 함수는 값이 변하지 않는 특징이 있어서 불필요한 렌더링이 발생하지 않음)
useImperativeHandle
- 부모 컴포넌트에서 자식 컴포넌트에 접근가능한 함수를 정의할때 사용
import React,{useState, useImperativeHandle, useRef} from "react";
// ref 속성명을 사용하고 싶다면, React.forwardRef() 함수 사용하기
export default function PublicChildComponent({publicChildComponentRef}) {
// App 컴포넌트에서 에서 ref 를 넘기지 않았다면 null 로 넘어와 에러가 일어나게 된다.
const [number, setNumber] = useState(0);
// 마치 함수형 컴포넌트를 클래스처럼 public / private 하게 사용가능하게 해준다.
// 1번째 인자 : ref
// 2번째 인자 : 외부로 공개할 함수(ref.current 로 접근)
useImperativeHandle(publicChildComponentRef, () => {
return {
setNumberValue : (targetNumber) => setNumber(targetNumber),
addNumberValue : () => setNumber(number + 1),
}
});
return (
<div>
number is {number}
</div>
)
}
- useImperativeHandle 훅은 첫번째 인자로 ref 객체를 받고, 두번째 인자로 부모 컴포넌트에서 접근가능한 함수를 객체의 메서드 형태로 작성한다.
- 부모 컴포넌트에서는 refObject.current.메서드명 으로 접근
- 자식 컴포넌트에 메서드를 외부에 보낼수 있으며, 내부에 공개하고 싶지 않은 메서드는 private 하게 관리 가능
useLayoutEffect
- useEffect 훅처럼 부수효과를 처리하는 훅. 다른점은, useEffect 훅은 비동기 / useLayoutEffect 훅은 동기로 처리
렌더링 후, DOM 요소를 조작할때 useLayoutEffect 훅을 사용하는것이 적합하지만 많은 연산처리를 하는경우 성능이슈가 있을수 있으니 주의해서 사용해야 한다.
import React,{useEffect, useLayoutEffect}from "react";
export default function UseLayoutEffectComponent() {
// rendering 결과가 DOM 에 반영된 후 비동기로 호출
// 일단 화면을 보여주고 실행
useEffect(() => {
// do side effects
console.log(`useEffect hook..`);
return () => {
}
}, []);
// rendering 후, DOM 에 반영되기 전에 동기로 호출
// 여기서 연산을 많이하면, 동기로 호출이 되기때문에 브라우저가 먹통이 되는 경우가 있으니 주의
// rendering 후, 변화과 완료된 DOM 을 보여주기 때문에 DOM 조작시 오히려 더 적합
// 모든 처리를 다 하고, 화면을 보여줌
useLayoutEffect(() => {
// do side effects
console.log(`useLayoutEffect hook..`);
return () => {
}
}, []);
console.log(`UseLayoutEffectComponent render..`);
return (
<div>
<p>
useLayoutEffect
</p>
<p>
콘솔창을 확인하면 <br/>
UseLayoutEffectComponent render..<br/>
useLayoutEffect hook....<br/>
useEffect hook..<br/>
</p>
</div>
)
}