본문 바로가기
개발개발/LifeGraph_인생그래프

[인생 그래프] 클릭한 위치에 컴포넌트 추가하기

by yelimu 2025. 2. 10.

클릭 시 Point 컴포넌트를 배치하는 것 + 반응형 구현을 용이하게 하려고 canvas를 사용하지 않고 일반 div를 사용하기로 했다.

 

클릭했을때 (잘 안보이지만) Point 컴포넌트가 호출되고, 그 내부에서 input에 title을 입력한다.

 

▼ Point 컴포넌트

더보기
import { ChangeEvent, useEffect, useRef } from "react";

const Point = ({
  x,
  y,
  title,
  onChange,
}: {
  x: number;
  y: number;
  title: string;
  onChange: (e: ChangeEvent<HTMLInputElement>) => void;
}) => {
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  return (
    <div
      style={{
        position: "absolute",
        width: "4px",
        height: "4px",
        color: "blue",
        top: `${y}px`,
        left: `${x}px`,
        borderRadius: "50%",
      }}
    >
      <input type="text" value={title} ref={inputRef} onChange={onChange} />
    </div>
  );
};

export default Point;

▼ Graph 컴포넌트

더보기
import { ChangeEvent, MouseEvent, useState } from "react";
import "./graph.scss";
import Point from "./Point";

interface PointData {
  id: number;
  x: number;
  y: number;
  title: string;
}

const Graph = () => {
  const [points, setPoints] = useState<PointData[]>([]);

  const handleClick = (e: MouseEvent<HTMLDivElement>) => {
    const rect = e.currentTarget.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;

    setPoints((prev) => [...prev, { id: Date.now(), x, y, title: "" }]); // 클릭한 좌표 저장
  };
  const handleChange = (e: ChangeEvent<HTMLInputElement>, id: number) => {
    setPoints((prev) => prev.map((point) => (point.id === id ? { ...point, title: e.target.value } : point)));
  };
  return (
    <div className="graph-container" onClick={handleClick}>
      {points.map((point) => (
        <Point key={point.id} x={point.x} y={point.y} title={point.title} onChange={(e) => handleChange(e, point.id)} />
      ))}
      <div className="graph-upper"></div>
      <div className="graph-below"></div>
    </div>
  );
};

export default Graph;

input에 입력한 값을 상위에서 관리하기 위해 points 에 id, title 필드가 추가되었고, handleChange를 prop으로 내려준다.

처음에 클릭했을때는 좌표 데이터를 받고, title은 빈 값""으로 저장한다. 

 

id로는 uuid 를 써도되지만 생성한 시간으로 설정하는 것도 좋은것같다


이전에 작성한 input을 수정하고싶은데 click 이벤트가 발동되어서 새로운 Point 컴포넌트가 생성된다.

 

Point 내부의 ref를 밖으로 빼고 forwardRef 로 감싸줘야겠다. 

Point 내부에서 input 요소에 onClick={(e)=>e.stopPropagation()} 하면 해결된다.

상위로 뺀 ref는 유지하기로 했다. 나중에 요소를 삭제하거나 이동할때 상위에서 관리하는게 낫지않을까?해서

 

이미지를 클릭하면 사이트로 이동합니다.