React hook을 이용한 코드 분리

Yuno
6 min readMay 7, 2020

--

React Hook이 등장하기 이전에는 state를 사용하거나 life cycle 관련 처리를 하려면 클래스 컴포넌트 사용이 강제 되었습니다.

React Hook의 등장

hook이 등장하면서 함수형 컴포넌트(FC)에서도 state, life cycle 등 클래스컴포넌트에서만 가능했던 것들을 사용할 수 있게 되었습니다.

그 뿐만 아니라 Hook을 이용해서 기능별로 코드를 쉽게 분리 할 수 있게 되었습니다.

React Hook이 필요한 순간

import React from "react"...function mapStateToProps(state: IAppState) {
return {
// Redux Store에서 가져올 데이터 맵핑
};
}function mapDispatchToProps(dispatch: AppDispatch){
return bindActionCreators({
dispatchGetData: getData,
},
dispatch,
);
}
class TestComponent extends React.PureComponent<{}> {
public componentDidMount() {
getInitializedData();
}
public render() {
return <div></div>
}
private readonly getInitializedData = () => {
this.props.dispatchGetData();
}
}
export default connect(
mapStateToProps,
mapDispatchToProps,
)(TestComponent);

위에 코드는 일반적인 class 기반의 컴포넌트에서 redux와 연결하고, 이를 통한 처리를 하는 코드입니다.

위의 코드는 그리 복잡해 보이지는 않지만,
만약 하나의 컴포넌트에서 가지고 있어야 되는 데이터의 종류가 다양해지고, 수행해야되는 일이 많아지게 된다면 어떻게 될까요?

코드는 복잡해 질 것이고, 그걸 보는 다른 사람들도 이해하기 어려워지게 될 것 입니다.

하지만 Hook을 이용하면 각각의 역할(기능)에 맞게 분리할 수 있어서 코드를 이해하게 쉽게 만들어 주고, 재사용성, 가독성까지 높일 수 있습니다.

React Hook을 이용해서 코드를 분리해보자 !

우선 FC에서 dispatchAction과 ReduxStore에 접근하기 해야되기 때문에 이에 맞는 hook을 만들어 줍니다.

...export function useActions<T extends ActionCreatorsMapObject>(
actions: T,
deps: any[] = [],
) {
const dispatch = useDispatch<AppDispatch>();
return React.useMemo(
() => bindActionCreators<T>(actions, dispatch),
deps ? [...deps, dispatch] : [dispatch],
);
}
export function useReduxState<T>(
mapStateSelector: (state: IAppState) => T,
equalityFn?: (left: T, right: T) => boolean,
): H {
return useSelector(mapStateSelector, equalityFn);
}

이제 useActions, useReduxState hook으로 FC에서도 Redux를 사용할 수 있게 되었습니다.

이 다음에는 처음에 보았던 컴포넌트를 역할(기능)에 따라 props, handlers, effects로 분리할 것 입니다.

여기서 props는 해당 컴포넌트에서 사용하는 데이터를 말하고,
handlers는 메소드와 같이 데이터를 사용하는 함수들를,
effects는 라이프사이클 처럼 상태가 변할 때 해야되는 작업을 의미합니다.

이 3가지 역할(역할)에 따라 코드를 분리해보면 아래와 같은 코드가 됩니다.

import React from "react"...type IHookProps = ReturnType<typeof useProps>;
type IHookHandlers = ReturnType<typeof useHandlers>;
// props
function useProps(props: IProps) {
const states = useStoreState(state => ({
// Redux Store에서 가져올 데이터 맵핑
}));
const actions = useActions({
dispatchGetData: getData,
});
const [state, setState] = React.useState(null); return {
...props,
...states,
...actions,
state,
setState,
}
}
// handlers
function useHandlers(props: IHookProps) {
const { dispatchGetData } = props;
const getInitializedData = React.useCallback(() => {
dispatchGetData();
}, [dispatchGetData]);
return { getInitializedData }
}
// effects
function useEffects(props: IHookProps, handlers: IHookHandlers){
const { getInitializedData } = handlers;
React.useEffect(() => { getInitializedData() }, [])
}
const TestComponent: React.FC<IProps> = (props) => {
const hookProps = useProps(props);
const hookHandlers = useHandlers(hookProps);
useEffects(hookProps, hookHandlers);

return <div></div>
}

어떤가요 ?

처음에 봤던 코드보다 길어졌다고 느낄 수는 있지만, props, handlers, effects와 같이 역할에 맞게 분리되어서 코드가 훨씬 깔끔하고, 보기 편해졌습니다.

여기서 더 나아가
propshooks/props.ts 파일에,
handlershooks/handlers.ts 파일에,
effectshooks/effects.ts 파일에 분리하면

index.tsx안에 있는 파일은 View와 관련된 렌더링 코드만 있게 되어 코드를 좀 더 깔끔하고 가독성이 좋게 만들 수 있습니다.

결론

이렇게 Hook은 함수형 컴포넌트에서 state, life cycle과 같은 class에서만 사용할 수 있었던 기능을 사용하게 해주는 기능도 있지만,

더 나아가 Hook을 이용해서 기능들을 함수로 쉽게 분리할 수 있고, 이를 통해서 코드 재사용성을 높이고 가독성을 높일 수 있습니다.

--

--