Сохранить новые координаты при перемещении элемента в файл json по клику на кнопку (React JS)
У меня отрисовано несколько связанных между собой таблиц, которые могут перемещаться по экрану. Если я перемещаю таблицы, мне надо нажать на кнопку save, чтобы в файл json записались новые координаты всех таблиц (и старые, если я эти таблицы не перемещала).
Я прописала функцию SetPositions для этого сохранения. Пока она находилась в компоненте Draggable, координаты сохранялись верно, но только одной таблицы, и кнопки сохранения были на каждой из них. Теперь кнопка одна, находится в компоненте Tables, но координаты не сохраняются и я по клику получаю результат только таблицы SomeEx7.
Какие изменения стоит внести в код, чтобы реализовать мою задачу?
Tables.js
import React, { useState, createRef, forwardRef } from "react";
import { v4 as uuidv4 } from "uuid";
import styles from "./Tables.module.css";
import tableData from "../utils/all_db_schema.json";
import { DraggableBox } from "../Draggable/Draggable";
import Xarrow, { useXarrow, Xwrapper } from "react-xarrows";
import { saveAs } from "file-saver";
export const Tables = () => {
const [tableState, setTableState] = useState(tableData);
const [upd, setUpd] = useState(false);
const ref = createRef();
var positionSettings = {
settingsId: uuidv4(),
tables: [
{
tableName: "",
newCoordinates: {
x: "",
y: "",
},
},
],
};
function SetPositions() {
var positions = [];
positions.push(ref.current.getBoundingClientRect());
//var newPosition = positions.slice(-1);
var newX = positions.map((el) => el.x.toString());
var newY = positions.map((el) => el.y.toString());
tableState.map((area) => {
area.tables.map((table) => {
const myTable = table;
positionSettings.tables.map((table) => {
table.tableName = myTable.tableName.toString();
table.newCoordinates.x = newX.join();
table.newCoordinates.y = newY.join();
});
})
})
var settings = new Blob([JSON.stringify(positionSettings)], {
type: "application/json",
name: "settings",
});
let fileName = "settings.json";
saveAs(settings, fileName);
};
return (
<>
{tableState.map((elem, index) => (
<div
className={styles.AreaContainer}
key={index}
style={{ color: elem.color }}
onMouseMove={() => setUpd(!upd)}
>
<button onClick={() => SetPositions()}>
save
</button>
{/*<p>Имя: {elem.groupName}</p>*/}
<div className={styles.ClassesContainer}>
{elem.tables.map((table, index) => (
<DraggableBox table={table} key={index} ref={forwardRef} />
))}
{elem.tables.map((table, index) => {
var components = [];
table.constraints.forEach((constrain) => {
components.push(<Xarrow start={table.tableName} end={constrain} path={"straight"} color={"black"} strokeWidth={1} />)
})
return (
components
)
}
)}
</div>
</div>
))}
</>
);
};
Draggable.js
import Draggable from "react-draggable";
import React from "react";
import Xarrow, { useXarrow, Xwrapper } from "react-xarrows";
import { Column } from "../Column/Column";
import styles from "./Draggable.module.css";
export const DraggableBox = ({ table, key, ...style }) => {
const { tableName, coordinates } = table;
const updateXarrow = useXarrow();
return (
<Draggable onDrag={updateXarrow} onStop={updateXarrow}>
<div
id={tableName}
style={{
position: "absolute",
left: coordinates.x + "px",
top: coordinates.y + "px",
border: "2px solid",
}}
>
<div className={styles.TableHead}>
<p>Название: {tableName}</p>
</div>
<div>
{table.columns.map((column, index) => (
<Column column={column} key={index} />
))}
</div>
</div>
</Draggable>
);
};
Пыталась также передать функцию от дочернего к родительскому, но выдает ошибку, что не видит ref
const DraggableBox = ({ table, ref, ...style }) => {
const { tableName, coordinates } = table;
const draggableRef = useRef();
const updateXarrow = useXarrow();
var positionSettings = {
settingsId: uuidv4(),
tables: [
{
tableName: "",
newCoordinates: {
x: "",
y: "",
},
},
],
};
function SetPositions() {
var positions = [];
positions.push(draggableRef.current.getBoundingClientRect());
var newPosition = positions.slice(-1);
var newX = newPosition.map((el) => el.x.toString());
var newY = newPosition.map((el) => el.y.toString());
positionSettings.tables.map((table) => {
table.tableName = tableName.toString();
table.newCoordinates.x = newX.join();
table.newCoordinates.y = newY.join();
});
var settings = new Blob([JSON.stringify(positionSettings)], {
type: "application/json",
name: settings,
});
let fileName = "settings.json";
saveAs(settings, fileName);
//localStorage.setItem('newCoordinates', JSON.stringify(positionSettings))
//console.log({ positionSettings });
};
useImperativeHandle(ref, () => ({
SetPositions
}))
return (
<Draggable onDrag={updateXarrow} onStop={updateXarrow}>
<div
id={tableName}
draggableRef={draggableRef}
style={{
position: "absolute",
left: coordinates.x + "px",
top: coordinates.y + "px",
border: "2px solid",
}}
>
<div className={styles.TableHead}>
<p>Название: {tableName}</p>
</div>
<div>
{table.columns.map((column, index) => (
<Column column={column} key={index} />
))}
</div>
</div>
</Draggable>
);
};
и вот родительский компонент:
export const Tables = () => {
const [tableState, setTableState] = useState(tableData);
const [upd, setUpd] = useState(false);
var childRef = useRef();
function GetPositions() {
childRef.current?.SetPositions()
}
return (
<>
{tableState.map((elem, index) => (
<div
className={styles.AreaContainer}
key={index}
style={{ color: elem.color }}
onMouseMove={() => setUpd(!upd)}
>
<button onClick={() => GetPositions()}>save</button>
{/*<p>Имя: {elem.groupName}</p>*/}
<div className={styles.ClassesContainer} >
{elem.tables.map((table, index) => (
<DraggableBox table={table} key={index} ref={childRef} />
))}
{elem.tables.map((table, index) => {
var components = [];
table.constraints.forEach((constrain) => {
components.push(
<Xarrow
start={table.tableName}
end={constrain}
path={"straight"}
color={"black"}
strokeWidth={1}
/>
);
});
return components;
})}
</div>
</div>
))}
</>
);
};
Ответы (1 шт):
Ниже показана возможная реализация общения дочернего компонента с родительским. Код условный и его надо адаптировать под вашу структуру данных.
Файл Tables.js
// В эту переменную положите ваши настройки таблиц
const [tablesPosition, setTablesPosition] = useState(tablePositions)
// `tablePositions` это те данные, которые вы извлекаете в рендере `elem.tables`
// Создаем функцию, которая обновляет положение таблиц при перемещении
const setNewPosition = (tableName, x, y) => {
// Код ниже условный, т.к. вашу структуру хранения данных не знаю
const tableState = tablesPosition.map((table) => {
if (table.tableName === tableName) {
table.coordiates.x = x;
table.coordiates.y = y;
}
return table;
})
setTablesPosition(tableState);
}
// В компонент `DraggableBox` передаем функцию setNewPosition
<DraggableBox table={table} key={index} ref={childRef} changePos={setNewPosition} />
Файл Draggable.js
// Добавляем пропс к DraggableBox
const DraggableBox = ({ table, ref, changePos, ...style })
// Создаем функцию для события onStop
const updatePosition = (e) {
setNewPosition(tableName, e.x, e.y);
updateXarrow()
}
// Меняем функцию в событии onStop
<Draggable onDrag={updateXarrow} onStop={updatePosition}>
Теперь, после каждого передвижения таблиц, компонент будет сообщать родителю(вызывать переданную функцию), что положение изменилось. Данные о новом положении будут записаны в state компонента Tables в переменную tablesPosition.
Остается только по нажатию вашей кнопки "сохранить", взять данные из tablesPosition и сохранить их.
