Merge pull request #5703 from xyflow/fix/use-nodes-data-type
Improve return type of useNodesData
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
'@xyflow/react': patch
|
||||||
|
'@xyflow/svelte': patch
|
||||||
|
'@xyflow/system': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Improve return type of useNodesData. Now you can narrow down the data type by checking the node type.
|
||||||
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { memo, useState } from 'react';
|
import { memo, useState } from 'react';
|
||||||
import { Position, NodeProps, Handle, useReactFlow } from '@xyflow/react';
|
import { Position, NodeProps, Handle, useReactFlow } from '@xyflow/react';
|
||||||
|
import type { TextNode } from '.';
|
||||||
|
|
||||||
function TextNode({ id, data }: NodeProps) {
|
function TextNode({ id, data }: NodeProps<TextNode>) {
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
const [text, setText] = useState(data.text);
|
const [text, setText] = useState(data.text);
|
||||||
const updateText = (text: string) => {
|
const updateText = (text: string) => {
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { memo, useEffect } from 'react';
|
import { memo, useEffect } from 'react';
|
||||||
import { Position, NodeProps, useReactFlow, Handle, useNodeConnections, useNodesData } from '@xyflow/react';
|
import { Position, NodeProps, useReactFlow, Handle, useNodeConnections, useNodesData } from '@xyflow/react';
|
||||||
import { isTextNode, type TextNode, type MyNode } from '.';
|
import { isTextNode, type TextNode, type MyNode, type UppercaseNode } from '.';
|
||||||
|
|
||||||
function UppercaseNode({ id }: NodeProps) {
|
function UppercaseNode({ id }: NodeProps<UppercaseNode>) {
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
const connections = useNodeConnections({
|
const connections = useNodeConnections({
|
||||||
handleType: 'target',
|
handleType: 'target',
|
||||||
});
|
});
|
||||||
const nodesData = useNodesData<MyNode>(connections[0]?.source);
|
const nodesData = useNodesData<MyNode>(connections[0]?.source);
|
||||||
const textNode = isTextNode(nodesData) ? nodesData : null;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
updateNodeData(id, { text: textNode?.data.text.toUpperCase() });
|
const text = nodesData?.type === 'text' ? nodesData?.data.text.toUpperCase() : undefined;
|
||||||
}, [textNode]);
|
updateNodeData(id, { text });
|
||||||
|
}, [nodesData]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ background: '#eee', color: '#222', padding: 10, fontSize: 12, borderRadius: 10 }}>
|
<div style={{ background: '#eee', color: '#222', padding: 10, fontSize: 12, borderRadius: 10 }}>
|
||||||
|
|||||||
@@ -8,22 +8,21 @@
|
|||||||
type Node,
|
type Node,
|
||||||
type NodeProps
|
type NodeProps
|
||||||
} from '@xyflow/svelte';
|
} from '@xyflow/svelte';
|
||||||
import { isTextNode, type MyNode } from './+page.svelte';
|
import { type MyNode } from './+page.svelte';
|
||||||
|
|
||||||
let { id }: NodeProps<Node<{ text: string }>> = $props();
|
let { id }: NodeProps<Node<{ text: string }>> = $props();
|
||||||
|
|
||||||
const { updateNodeData } = useSvelteFlow();
|
const { updateNodeData } = useSvelteFlow();
|
||||||
const connections = useNodeConnections({
|
const connections = useNodeConnections({
|
||||||
id: id,
|
|
||||||
handleType: 'target'
|
handleType: 'target'
|
||||||
});
|
});
|
||||||
|
|
||||||
let nodeData = $derived(useNodesData<MyNode>(connections.current[0]?.source));
|
let nodesData = $derived(useNodesData<MyNode>(connections.current[0]?.source));
|
||||||
let textNodeData = $derived(isTextNode(nodeData.current) ? nodeData.current.data.text : null);
|
|
||||||
|
|
||||||
$effect.pre(() => {
|
$effect.pre(() => {
|
||||||
const input = textNodeData?.toUpperCase() ?? '';
|
const text =
|
||||||
updateNodeData(id, { text: input });
|
nodesData.current?.type === 'text' ? nodesData.current?.data.text.toUpperCase() : undefined;
|
||||||
|
updateNodeData(id, { text });
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { shallowNodeData } from '@xyflow/system';
|
import { DistributivePick, shallowNodeData } from '@xyflow/system';
|
||||||
|
|
||||||
import { useStore } from '../hooks/useStore';
|
import { useStore } from '../hooks/useStore';
|
||||||
import type { Node } from '../types';
|
import type { Node } from '../types';
|
||||||
@@ -25,11 +25,11 @@ import type { Node } from '../types';
|
|||||||
export function useNodesData<NodeType extends Node = Node>(
|
export function useNodesData<NodeType extends Node = Node>(
|
||||||
/** The id of the node to get the data from. */
|
/** The id of the node to get the data from. */
|
||||||
nodeId: string
|
nodeId: string
|
||||||
): Pick<NodeType, 'id' | 'type' | 'data'> | null;
|
): DistributivePick<NodeType, 'id' | 'type' | 'data'> | null;
|
||||||
export function useNodesData<NodeType extends Node = Node>(
|
export function useNodesData<NodeType extends Node = Node>(
|
||||||
/** The ids of the nodes to get the data from. */
|
/** The ids of the nodes to get the data from. */
|
||||||
nodeIds: string[]
|
nodeIds: string[]
|
||||||
): Pick<NodeType, 'id' | 'type' | 'data'>[];
|
): DistributivePick<NodeType, 'id' | 'type' | 'data'>[];
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export function useNodesData(nodeIds: any): any {
|
export function useNodesData(nodeIds: any): any {
|
||||||
const nodesData = useStore(
|
const nodesData = useStore(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { shallowNodeData } from '@xyflow/system';
|
import { shallowNodeData, type DistributivePick } from '@xyflow/system';
|
||||||
|
|
||||||
import type { Node } from '$lib/types';
|
import type { Node } from '$lib/types';
|
||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
@@ -12,10 +12,10 @@ import { useStore } from '$lib/store';
|
|||||||
*/
|
*/
|
||||||
export function useNodesData<NodeType extends Node = Node>(
|
export function useNodesData<NodeType extends Node = Node>(
|
||||||
nodeId: string
|
nodeId: string
|
||||||
): { current: Pick<NodeType, 'id' | 'data' | 'type'> | null };
|
): { current: DistributivePick<NodeType, 'id' | 'data' | 'type'> | null };
|
||||||
export function useNodesData<NodeType extends Node = Node>(
|
export function useNodesData<NodeType extends Node = Node>(
|
||||||
nodeIds: string[]
|
nodeIds: string[]
|
||||||
): { current: Pick<NodeType, 'id' | 'data' | 'type'>[] };
|
): { current: DistributivePick<NodeType, 'id' | 'data' | 'type'>[] };
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export function useNodesData(nodeIds: any): any {
|
export function useNodesData(nodeIds: any): any {
|
||||||
const { nodes, nodeLookup } = $derived(useStore());
|
const { nodes, nodeLookup } = $derived(useStore());
|
||||||
|
|||||||
@@ -56,3 +56,10 @@ export type Transform = [number, number, number];
|
|||||||
* to represent an unbounded extent.
|
* to represent an unbounded extent.
|
||||||
*/
|
*/
|
||||||
export type CoordinateExtent = [[number, number], [number, number]];
|
export type CoordinateExtent = [[number, number], [number, number]];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Using Pick with a union type (e.g. `NodeType`) will merge every property type along all union members.
|
||||||
|
* See https://github.com/microsoft/TypeScript/issues/28339#issuecomment-463577347
|
||||||
|
* Note: Currently you are able to Pick properties that are not in the type without error.
|
||||||
|
*/
|
||||||
|
export type DistributivePick<T, K extends string> = T extends unknown ? { [P in Extract<keyof T, K>]: T[P] } : never;
|
||||||
|
|||||||
Reference in New Issue
Block a user