d3 + react 유용한 hooks

d3와 react를 사용할 때에 유용하게 쓰일 수 있는 몇 가지 custom hooks이 있다.

useResizeObserver

d3로 만든 차트가 반응형으로 작동하도록 하는 hooks이다.

해당 html요소를 관찰하다 크기변화가 발생하면 내부의 callback을 실행한다.

svg의 width가 100%여야 정상적으로 작동한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { useEffect, useState } from "react";
import ResizeObserver from "resize-observer-polyfill";

export const useResizeObserver = (ref) => {
const [dimensions, setDimensions] = useState(null);
useEffect(() => {
const observeTarget = ref.current;
const resizeObserver = new ResizeObserver((entries) => {
entries.forEach((entry) => {
setDimensions(entry.contentRect);
});
});
resizeObserver.observe(observeTarget);
return () => {
resizeObserver.unobserve(observeTarget);
};
}, [ref]);
return dimensions;
};

export default useResizeObserver;

usePrevValue

이전 렌더링의 값을 저장하는 hooks이다.

useEffect 내부에서 조건부 작업을 할 경우에 사용하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
import { useEffect, useRef } from "react";

function usePrevValue(value) {
const prevRef = useRef(null);

useEffect(() => {
prevRef.current = value;
});

return prevRef.current;
}

export default usePrevValue;

useDebounce

d3에는 수많은 이벤트가 발생한다!

zoom 이벤트나 위에서 언급한 useResizeObserver의 resize이벤트등의 요청을 줄일 수 있는 hook이다.

delay 안에 새로운 이벤트가 발생하면 기존의 값을 리턴한다.

마지막 요청 이후 delay만큼의 시간이 지나면 새롭게 세팅된 값을 리턴한다.

즉 디바운스를 hook으로 만들었다!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { useEffect, useState } from "react";

export const useDebounce = (value, delay) => {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);

return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
};

export default useDebounce;

적용하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const App = ({ data }) => {
const containerRef = useRef();
const wrapperRef = useRef();
const dimensions = useResizeObserver(wrapperRef);
const resize = useDebounce(dimensions, 200);

useEffect(() => {
const svg = select(svgRef.curent);
const container = select(containerRef.current);

if (!resize) return;

const { width, height } = resize;
svg.attr("width", width).attr("height", height);
}, [data, resize]);

return (
<div ref={containerRef}>
<svg ref={svgRef} />
</div>
);
};

댓글