Przenoszenie funkcji komponentu do oddzielnego pliku

Przenoszenie funkcji komponentu do oddzielnego pliku

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

Gouda105
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 487
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ć.

Kopiuj
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>
	);
}
LukeJL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 8488
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.

Kopiuj
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:

Kopiuj
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:

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

i potem przekazywać ten cały obiekt.

poza tym zamiast:

Kopiuj
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

Gouda105
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 487
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?

LukeJL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 8488
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ę.

Gouda105
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 487
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?

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.