문제 상황
<StyledAgGrid
// 생략
headerHeight={showHeader ? headerHeight : 0}
// 생략
>
// 생략
이러한 코드의 예상 동작은 무엇일까요?
showHeader의 변경에 따라 headerHeight값이 변경되는것을 기대했습니다.
하지만 처음 showHeader이 변한 이후에는 값 변경이 일어나지 않았습니다.
분석 동기
1. AgGrid는 내부적으로 어떻게 되어있길래 처음에 한대로 props에 조건에 따라 headerHeight 업데이트가 안되는지 알기 위함
2. 직접 ref로 접근하는게 AgGrid철학에 맞는것인지 파악하기 위함
공식 문서 참고
왜 그런지 공식 문서를 살펴보겠습니다.
https://www.ag-grid.com/react-data-grid/grid-options/
https://www.ag-grid.com/react-data-grid/grid-interface/#updating-grid-options
React Grid: Grid Overview | AG Grid
Browse all the React Table Grid interfaces. Configure and interact with the grid using Grid Options, Grid Events, Grid State, and the Grid API. Download AG Grid v33.2.4 today: The best React Table & React Data Grid in the world.
www.ag-grid.com
일부 Grid 옵션은 값 변경을 지원하지 않습니다.
이러한 옵션은 그리드 초기 설정 시에만 읽히며,
옵션 참조 문서에서 ‘Initial’로 표시되어 있습니다.
하지만 headerHeight에는 Inital표시가 없는데 왜 초기에만 반영이 될까요?
해당 내용은 없는것 같습니다.
그래서 코드를 살펴봤습니다.
내부 코드 로직 분석
AgGridReact는 React의 Component를 상속받아 정의되며, 내부적으로 AgGridReactUi를 렌더링합니다. 따라서 실질적인 렌더링과 초기화 로직이 구현된 AgGridReactUi 컴포넌트를 중심으로 살펴보겠습니다.
import React, { Component } from 'react';
import type { GridApi } from 'ag-grid-community';
import { AgGridReactUi } from './reactUi/agGridReactUi';
import type { AgGridReactProps } from './shared/interfaces';
export class AgGridReact<TData = any> extends Component<AgGridReactProps<TData>, object> {
/** Grid Api available after onGridReady event has fired. */
public api!: GridApi<TData>;
private apiListeners: Array<(params: any) => void> = [];
public registerApiListener(listener: (api: GridApi) => void) {
this.apiListeners.push(listener);
}
private setGridApi = (api: GridApi) => {
this.api = api;
this.apiListeners.forEach((listener) => listener(api));
};
override componentWillUnmount() {
this.apiListeners.length = 0;
}
override render() {
return <AgGridReactUi<TData> {...this.props} passGridApi={this.setGridApi} />;
}
}
https://github.com/ag-grid/ag-grid/blob/latest/packages/ag-grid-react/src/agGridReact.tsx
// ag-grid/packages/ag-grid-react/src/reactUi/agGridReactUi.tsx
// 생략
const DetailCellRenderer = forwardRef((props: IDetailCellRendererParams, ref: any) => {
// 생략
const setRef = useCallback((eRef: HTMLDivElement | null) => {
eGui.current = eRef;
if (!eRef) {
destroyFuncs.current.forEach((f) => f());
destroyFuncs.current.length = 0;
return;
}
// 생략
const gridCoreCreator = new GridCoreCreator();
// We ensure that the gridId is stable even in StrictMode
mergedGridOps.gridId ??= gridIdRef.current;
apiRef.current = gridCoreCreator.create(
eRef,
mergedGridOps,
createUiCallback,
acceptChangesCallback,
gridParams
);
destroyFuncs.current.push(() => {
apiRef.current = undefined;
});
if (apiRef.current) {
gridIdRef.current = apiRef.current.getGridId();
}
}, []);
// 생략
return (
<div className={topClassName} ref={setRef}>
{detailGridOptions && (
<AgGridReactUi
className={gridClassName}
{...detailGridOptions}
modules={parentModules}
rowData={detailRowData}
passGridApi={registerGridApi}
/>
)}
</div>
);
});
https://github.com/ag-grid/ag-grid/blob/latest/packages/ag-grid-react/src/reactUi/agGridReactUi.tsx
GridOptions는 다음 링크에서 headerHeight도 포함을 확인할 수 있습니다.
https://github.dev/ag-grid/ag-grid/blob/latest/packages/ag-grid-react/src/reactUi/agGridReactUi.tsx
apiRef.current = gridCoreCreator.create 이 코드 부분으로 mergedGridOps을 설정하기 때문에 초기에 설정되고, 이후에 ref를 최신화해야 반영되는것으로 파악하였습니다.
예를 들어, 이런식으로 코드를 작성하는것입니다.
useEffect(() => {
// 전체 헤더 활성화 토글시 비활성화 이후 계속 헤더가 안나오는 이슈 있어서 추가.
if (gridApiRef.current) {
gridApiRef.current.setHeaderHeight(showHeader ? headerHeight : 0);
}
}, [showHeader, headerHeight]);
라이브러리에서 자체적으로 header을 토글하는 api도 따로 없는것 같습니다.
https://www.ag-grid.com/javascript-data-grid/grid-api/
결론
분석후 내린 결론은 다음과 같습니다.
1. AG Grid는 React와 완전히 동일한 방식으로 동작하지 않습니다.
-> 렌더링은 React로 이루어지지만, 동작 흐름은 자체 로직으로 처리되는듯 합니다. 그 이유는 React가 존재하기 전부터 기존에 쌓아온 최적화 로직 때문일 것으로 예상하는데 그 부분을 찾아보고 싶었지만 더 분석이 필요합니다.
2. 일부 props는 초기 렌더링 시에만 반영되며, 이후 React에서 해당 prop 값을 변경해도 그리드에 자동 반영되지 않습니다.
-> 예) headerHeight등 일부 gridOptions 항목
3. 이러한 props는 AgGridReactUi 컴포넌트 내에서 초기 setRef 호출을 통해 한 번 설정되며, 이후 변경하려면 AG Grid의 API를 사용하여 수동으로 업데이트해야 합니다.
-> 예) gridApi.setHeaderHeight 호출 필요
댓글