Wątek przeniesiony 2023-05-14 09:47 z JavaScript przez Riddle.

Przenoszenie funkcji komponentu do oddzielnego pliku

0

Witam,
korzystam z React-Flow i stworzyłem diagram interaktywny. Jednaest wewnątrz komponentu, a ja chciałbym ją przenieść do osobnych plików. Jednak problem w tym, że te funkcje korzystają z hooków. Tak więc może ktoś mi podpowie jak przenieść funkcje getChildNodePosition, onConnectEnd do osobnych plików?

Niby można wszystkie zależności przekazać w parametrach, ale wtedy było by ich z 10. Zamiast tego wolałbym na przykład wywołać const store = useStoreApi(); w osobnym pliku i na tym operować.

import React, {useCallback, useRef} from 'react';
import ReactFlow, {Controls, Panel, useStoreApi, useReactFlow, ConnectionLineType} from 'reactflow';
import {addChildNode, onEdgesChange, onNodesChange} from '../store/slices/mindMapSlice';
import {useDispatch, useSelector} from 'react-redux';

import MindMapNode from './Utils/MindMapNode';
import MindMapEdge from './Utils/MindMapEdge';

// we need to import the React Flow styles to make it work
import 'reactflow/dist/style.css';

const nodeTypes = {
	mindmap: MindMapNode,
};

const edgeTypes = {
	mindmap: MindMapEdge,
};

const nodeOrigin = [0.5, 0.5];
const connectionLineStyle = {stroke: '#F6AD55', strokeWidth: 3};
const defaultEdgeOptions = {style: connectionLineStyle, type: 'mindmap'};

export default function MindMap() {
	const dispatch = useDispatch();
	const {nodes, edges} = useSelector((state) => state.mindmap);
	const connectingNodeId = useRef(null);
	const store = useStoreApi();
	const {project} = useReactFlow();

	const getChildNodePosition = (event, parentNode) => {
		const {domNode} = store.getState();

		if (!domNode || !parentNode?.positionAbsolute || !parentNode?.width || !parentNode?.height) {
			return;
		}

		const {top, left} = domNode.getBoundingClientRect();

		const panePosition = project({
			x: event.clientX - left,
			y: event.clientY - top,
		});

		return {
			x: panePosition.x - parentNode.positionAbsolute.x + parentNode.width / 2,
			y: panePosition.y - parentNode.positionAbsolute.y + parentNode.height / 2,
		};
	};

	const onConnectStart = useCallback((_, {nodeId}) => {
		connectingNodeId.current = nodeId;
	}, []);

	const onConnectEnd = useCallback(
		(event) => {
			const {nodeInternals} = store.getState();
			const targetIsPane = event.target.classList.contains('react-flow__pane');
			const node = event.target.closest('.react-flow__node');

			if (node) {
				node.querySelector('input')?.focus({preventScroll: true});
			} else if (targetIsPane && connectingNodeId.current) {
				const parentNode = nodeInternals.get(connectingNodeId.current);
				const childNodePosition = getChildNodePosition(event, parentNode);

				if (parentNode && childNodePosition) {
					dispatch(addChildNode({parentNode, childNodePosition}));
				}
			}
		},
		[getChildNodePosition]
	);

	return (
		<ReactFlow
			nodes={nodes}
			edges={edges}
			onNodesChange={(d) => dispatch(onNodesChange(d))}
			onEdgesChange={(d) => dispatch(onEdgesChange(d))}
			nodeTypes={nodeTypes}
			edgeTypes={edgeTypes}
			onConnectStart={onConnectStart}
			onConnectEnd={onConnectEnd}
			connectionLineStyle={connectionLineStyle}
			defaultEdgeOptions={defaultEdgeOptions}
			connectionLineType={ConnectionLineType.Straight}
			nodeOrigin={nodeOrigin}
			fitView
		>
			<Controls showInteractive={false} />
			<Panel position="top-left" className="header">
				React Flow Mind Map
			</Panel>
		</ReactFlow>
	);
}
0

Ale co ty robisz w tych handlerach? Widzę, że strasznie mocno korzystasz z DOM, co jeszcze byłoby zrozumiałe, gdybyś w tym komponencie łączył jakieś imperatywne API oparte o DOM (np. używał jakiegoś widżetu nie-reactowego), ale że nic takiego tam nie widzę, to zaczynam przypuszczać, że w tym leży problem.

const targetIsPane = event.target.classList.contains('react-flow__pane');

Czyli wchodzisz w szczegóły implementacyjne biblioteki. Słaba praktyka. Pewnie, czasem nie ma innego wyjścia, ale czy to jest ten przypadek? Bo to nie jest normalne.

Zacząłbym najpierw od przejrzenia dokumentacji tej biblioteki, żeby zobaczyć, czy tego, co chcesz zrobić, nie da się zrobić bez używania takich sztuczek.

edit:
chociaż jak patrzę, to podobny przykład można znaleźć w dokumentacji:
https://reactflow.dev/docs/examples/nodes/add-node-on-edge-drop/
co jest dla mnie WTFem
przynajmniej ta linijka:

const targetIsPane = event.target.classList.contains('react-flow__pane');

bo to znaczy, że biblioteka sama zachęca do wchodzenia w jej internale? To nie jest dobre podejście autorów tej biblioteki w takim razie (albo autor przykładu nawalił). Bo teraz nie będą mogli zmienić klas CSS, z których korzystają, bez rozwalania kompatybilności wstecznej.

Niby można wszystkie zależności przekazać w parametrach, ale wtedy było by ich z 10.

Jak z 10? Nie widzę, żeby było aż z 10. Raczej 1-2 dodatkowe parametry, ale może źle to obczaiłem. Jakby parametrów było dużo, możesz je wepchać w obiekt:

const config = {foo, bar, baz, qwe, rty};

i potem przekazywać ten cały obiekt.

poza tym zamiast:

const {domNode} = store.getState();

lepiej żeby funkcja dostawała faktycznie te zmienne, z których faktycznie korzysta (literka I w SOLID).
A ta funkcja nie potrzebuje całego store, skoro potrzebuje domNode

0

@LukeJL: Cały przykład skopiowałem na szybko z oficjalnego przykładu https://reactflow.dev/blog/mind-map-app-with-react-flow/ i tylko zmieniłem go, żeby korzystał z Redux. Zrobiłem tak w ramach przygotowania do projektu i znalezienia najlepszej opcji na jeden z jego komponentów, którym ma być prosty generator mapy myśli. Ogólnie strasznie mi się nie podoba środowisko tej biblioteki i cały czas szukam alternatywy bardziej intuicyjnej. Może Ty możesz polecić mi jakąś do generowania interaktywnych diagramów?

0

z podobnych widziałem rete https://rete.js.org/#/ coś w nim kiedyś się bawiłem.
chociaż wydaje mi się, że React Flow może mieć więcej opcji, tak na pierwszy rzut oka, może też się nią pobawię.

0

Naprawdę długo szukałem czegoś podobnego i nie natrafiłem na Rete. Fajnie wygląda, ale brakuje mu nieco do interaktywnego diagramu. Znasz może jakieś inne alternatywy?

1 użytkowników online, w tym zalogowanych: 0, gości: 1