분류React 아래의 글

react 17.2.4, typescript 4.3.5, ag-grid 26.1.0, openjdk 17, spring, ibatis, mariadb 기술 스택을 기반으로
react 테스트 수행화면에서 시나리오 목록의 ag-grid에 하나의 행의 체크 박스를 체크하고 상위에 있는 Result 버튼을 클릭하면 ag-grid의 test_id, scenario_id 파라메터 값을 가지고 Rsult 화면으로 이동하는 예제 만들어 주세요

You're absolutely right to point that out\! The error "react-router-dom has no exported member 'useNavigate'" typically indicates a version mismatch.

Here's why this error occurs and how to fix it:

The Problem: Version Mismatch

  • useNavigate is a hook introduced in react-router-dom v6.
  • If you're getting this error, it means you're likely trying to use useNavigate with an older version of react-router-dom, most commonly v5 or earlier. In v5, you would use useHistory instead.

The Solution: Upgrade react-router-dom to v6 (or later)

To resolve this, you need to upgrade your react-router-dom package to version 6 or newer.

  1. Uninstall the current version:

    npm uninstall react-router-dom
  2. Install the latest version (v6+):

    npm install react-router-dom

    This will install the latest stable version, which is currently v6.x.x.

  3. Install @types/react-router-dom (if using TypeScript):

    npm install @types/react-router-dom

    It's crucial to also update the type definitions to match the react-router-dom version. In some cases, react-router-dom v6 has its own built-in types, making @types/react-router-dom unnecessary or even problematic if it's an outdated version. If you still encounter type errors after upgrading, try removing @types/react-router-dom and see if the issue resolves.

Important Changes in react-router-dom v6:

When upgrading from v5 to v6, there are several breaking changes you need to be aware of and adapt your code for:

  • <Switch> is replaced by <Routes>:

    • Old (v5):

      import { Switch, Route } from 'react-router-dom';
      
      <Switch>
        <Route exact path="/" component={HomePage} />
        <Route path="/about" component={AboutPage} />
      </Switch>
    • New (v6):

      import { Routes, Route } from 'react-router-dom';
      
      <Routes>
        <Route path="/" element={<HomePage />} />
        <Route path="/about" element={<AboutPage />} />
      </Routes>
  • component prop is replaced by element prop: As shown above, instead of component={Component}, you now use element={<Component />}.
  • exact prop is no longer needed: In v6, routes are matched exactly by default. You can use the * wildcard for nested routes.
  • useHistory is replaced by useNavigate:

    • Old (v5):

      import { useHistory } from 'react-router-dom';
      const history = useHistory();
      history.push('/some-path');
    • New (v6):

      import { useNavigate } from 'react-router-dom';
      const navigate = useNavigate();
      navigate('/some-path');
  • Nested Routes: V6 has improved nested routing capabilities.

Summary of the fix for the provided code:

The code you provided already uses useNavigate, Routes, and the element prop, which means it's written for react-router-dom v6. Therefore, the most probable cause of the error is that your node_modules still contain an older version of react-router-dom. Simply running the npm uninstall and npm install commands as described above should resolve it.

테스트 계층 구조를 위한 AG-Grid 구현 예제
react 17.2.4, typescript 4.3.5, ag-grid 26.1.0, openjdk 17, spring, ibatis, mariadb 기술 스택을 기반으로 테스트 계층 구조(테스트계획-테스트시나리오-테스트케이스-테스트액티비티)를 하나의 AG-Grid에 표시하고 표시/숨김 기능 있고 그리드에서도 +,- 항목이 있어서 표시/숨김 기능이 있도록 소스 생성해주세요

"테스트계층 구조(테스트계획 → 시나리오 → 케이스 → 액티비티)"를 하나의 AG-Grid에 표시 + 표시/숨김 토글 + +, -로 트리 확장/축소 가능한 예제

TypeScript 4.x + React 17 + ag-Grid 환경에서 Scenario, Process (Target, No Start, Success, Failed, Error), Comparison (Target, Success, Failed) 항목을 2단 헤더 구조로 구성한 예제를 아래에 제공합니다.

아래 예제는 다음을 포함합니다:

ag-Grid 2단(header group) 구조 설정

React 17, TypeScript 환경

Scenario, Process, Comparison 3개의 그룹 컬럼

// App.tsx
import React from "react";
import { AgGridReact } from "ag-grid-react";
import "ag-grid-community/styles/ag-grid.css";
import "ag-grid-community/styles/ag-theme-alpine.css";
import { ColDef, ColGroupDef } from "ag-grid-community";

const App: React.FC = () => {
const columnDefs: (ColDef | ColGroupDef)[] = [

{
  headerName: "Scenario",
  children: [
    { headerName: "Scenario", field: "scenario", width: 150 }
  ],
},
{
  headerName: "Process",
  children: [
    { headerName: "Target", field: "processTarget", width: 120 },
    { headerName: "No Start", field: "processNoStart", width: 120 },
    { headerName: "Success", field: "processSuccess", width: 120 },
    { headerName: "Failed", field: "processFailed", width: 120 },
    { headerName: "Error", field: "processError", width: 120 },
  ],
},
{
  headerName: "Comparison",
  children: [
    { headerName: "Target", field: "comparisonTarget", width: 120 },
    { headerName: "Success", field: "comparisonSuccess", width: 120 },
    { headerName: "Failed", field: "comparisonFailed", width: 120 },
  ],
},

];

const rowData = [

{
  scenario: "Scenario A",
  processTarget: 100,
  processNoStart: 10,
  processSuccess: 80,
  processFailed: 5,
  processError: 5,
  comparisonTarget: 100,
  comparisonSuccess: 75,
  comparisonFailed: 25,
},
{
  scenario: "Scenario B",
  processTarget: 200,
  processNoStart: 20,
  processSuccess: 160,
  processFailed: 10,
  processError: 10,
  comparisonTarget: 200,
  comparisonSuccess: 150,
  comparisonFailed: 50,
},

];

return (

<div className="ag-theme-alpine" style={{ height: 400, width: "100%" }}>
  <AgGridReact
    columnDefs={columnDefs}
    rowData={rowData}
    domLayout="autoHeight"
  />
</div>

);
};

export default App;

react17, typescript, ag-grid 환경에서 this.apiSet.api.forEachNode((node) => { 하여 그리드의 항목 중 activityNo:string; 항목(숫자가 입력 또는 빈값) 중 가장 큰 값을 구하고 그리드 목록 중 activityNo 빈값이 있으면 가장 큰 값에서 +1씩 증가하며 세팅하는 예제 만들어 주세요

// ActivityGrid.tsx
import React, { useRef, useCallback } from 'react';
import { AgGridReact } from 'ag-grid-react';
import { AgGridReact as AgGridReactType } from 'ag-grid-react/lib/agGridReact';
import { ColDef } from 'ag-grid-community';

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';

interface RowDataType {
id: number;
activityNo: string;
name: string;
}

const initialRowData: RowDataType[] = [
{ id: 1, activityNo: '3', name: 'Task A' },
{ id: 2, activityNo: '', name: 'Task B' },
{ id: 3, activityNo: '10', name: 'Task C' },
{ id: 4, activityNo: '', name: 'Task D' },
{ id: 5, activityNo: '2', name: 'Task E' },
];

const columnDefs: ColDef[] = [
{ field: 'id' },
{ field: 'activityNo' },
{ field: 'name' },
];

const ActivityGrid = () => {
const gridRef = useRef<AgGridReactType>(null);

const fillActivityNumbers = useCallback(() => {

const api = gridRef.current?.api;
if (!api) return;

const nodes: RowDataType[] = [];
api.forEachNode(node => {
  if (node.data) {
    nodes.push(node.data);
  }
});

// 1. 최대 숫자 추출
const activityNumbers = nodes
  .map(row => parseInt(row.activityNo))
  .filter(num => !isNaN(num));

let max = activityNumbers.length > 0 ? Math.max(...activityNumbers) : 0;

// 2. 빈 값 채우기
nodes.forEach(row => {
  if (!row.activityNo || row.activityNo.trim() === '') {
    max += 1;
    row.activityNo = String(max);
  }
});

// 3. 변경 사항 적용
api.setRowData([...nodes]);

}, []);

return (

<div>
  <button onClick={fillActivityNumbers}>Fill Empty Activity Numbers</button>
  <div className="ag-theme-alpine" style={{ height: 300, width: 600 }}>
    <AgGridReact
      ref={gridRef}
      rowData={initialRowData}
      columnDefs={columnDefs}
      domLayout="autoHeight"
    />
  </div>
</div>

);
};

export default ActivityGrid;

이 다음 줄로 내려가는 이유는 기본적으로 block 요소이거나, 부모 레이아웃이 세로 방향으로 되어 있기 때문입니다. 같은 줄에 보이도록 하려면 inline 스타일이나 flex를 사용할 수 있습니다.

아래는 React 17 + TypeScript에서 과 입력 필드( 등)를 같은 라인에 표시하는 예제입니다.

✅ 예제 코드: FormLabel과 Input을 같은 라인에 표시
import React from 'react';
import { FormLabel } from '@mui/material'; // 또는 사용 중인 라이브러리의 FormLabel
import { TextField } from '@mui/material'; // 예시로 MUI 사용

const InlineFormExample: React.FC = () => {
return (

<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
  <FormLabel htmlFor="output">Output</FormLabel>
  <TextField id="output" variant="outlined" size="small" />
</div>

);
};

export default InlineFormExample;
✅ Tailwind 사용 시 예제
tsx
복사
편집

✅ 핵심 포인트
display: 'flex' 또는 className="flex": 요소를 한 줄로 배치합니다.

alignItems: 'center' 또는 items-center: 수직 정렬을 맞춥니다.

gap 또는 space-x-N: 라벨과 필드 사이 간격 설정.