xyflow / xyflow

React Flow | Svelte Flow - Powerful open source libraries for building node-based UIs with React (https://reactflow.dev) or Svelte (https://svelteflow.dev). Ready out-of-the-box and infinitely customizable.
https://xyflow.com
MIT License
25.94k stars 1.66k forks source link

Multiple Type Errors with simple example using xyflow/react in NextJS App #4694

Closed nickreynolds closed 1 month ago

nickreynolds commented 1 month ago

What platform were you using when you found the bug?

Live code example

No response

Describe the Bug

I'm seeing multiple type errors when trying to implement the examples in the Quickstart Guide (https://reactflow.dev/learn) within a NextJS app. I've created a small repo that demonstrates the errors that you can look at here: https://github.com/nickreynolds/nextjs-xyflow-error

I've included details on the errors here

error with useNodesState:

const initialNodes: Node[] = [];

const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);

Fails to compile with the following error:

Type error: Argument of type 'Node[]' is not assignable to parameter of type 'import("/Users/nick/dec/nextjs-xyflow-error/node_modules/.pnpm/@xyflow+react@12.3.1_@types+react@18.3.9_react-dom@18.3.1_react@18.3.1/node_modules/@xyflow/react/dist/esm/types/nodes").Node[]'.
  Type 'Node' is not assignable to type 'import("/Users/nick/dec/nextjs-xyflow-error/node_modules/.pnpm/@xyflow+react@12.3.1_@types+react@18.3.9_react-dom@18.3.1_react@18.3.1/node_modules/@xyflow/react/dist/esm/types/nodes").Node'.
    Type 'Node' is missing the following properties from type 'NodeBase<Record<string, unknown>, string>': id, position, data

Lines error with setEdges:

    const onConnect = useCallback(
        (params) => setEdges((eds) => addEdge(params, eds)),
        [setEdges],
    );

Gives a type error of:

Parameter 'params' implicitly has an 'any' type.

error with nds.concat(newNode):

setNodes((nds) => nds.concat(newNode));

Gives an error of:

No overload matches this call.
  Overload 1 of 2, '(...items: ConcatArray<Node>[]): Node[]', gave the following error.
    Argument of type '{ id: string; position: XYPosition; data: { sp123ID: string; }; origin: number[]; type: string; }' is not assignable to parameter of type 'ConcatArray<Node>'.
      Type '{ id: string; position: XYPosition; data: { sp123ID: string; }; origin: number[]; type: string; }' is missing the following properties from type 'ConcatArray<Node>': length, join, slice
  Overload 2 of 2, '(...items: (Node | ConcatArray<Node>)[]): Node[]', gave the following error.
    Argument of type '{ id: string; position: XYPosition; data: { sp123ID: string; }; origin: number[]; type: string; }' is not assignable to parameter of type 'Node | ConcatArray<Node>'.
      Type '{ id: string; position: XYPosition; data: { sp123ID: string; }; origin: number[]; type: string; }' is not assignable to type 'Node'.
        Type '{ id: string; position: XYPosition; data: { sp123ID: string; }; origin: number[]; type: string; }' is not assignable to type 'NodeBase<Record<string, unknown>, string>'.
          Types of property 'origin' are incompatible.
            Type 'number[]' is not assignable to type '[number, number]'.
              Target requires 2 element(s) but source may have fewer.

Error with <Background variant="dots"...:

<Background variant="dots" gap={12} size={1} />

Gives an error of:

Type '"dots"' is not assignable to type 'BackgroundVariant | undefined'.ts(2322)
types.d.ts(29, 5): The expected type comes from property 'variant' which is declared here on type 'IntrinsicAttributes & BackgroundProps'

Steps to reproduce the bug or issue

  1. Clone repo
  2. Install dependencies (pnpm i)
  3. Build app (pnpm build)
  4. See Errors (compiler fails after first reported error so will only report the first, but others can be seen in an IDE)

Expected behavior

I would expect the simple example to compile correctly

Screenshots or Videos

No response

Additional context

No response

hsab commented 1 month ago

You're not importing the Node typpe, but won't get an error because it's defined in DOM. Code below should fix your errors. Also origin is set on the ReactFlow component, not per-node. Finally, if you wanna go deep try doing your own state management, useNodesState and useEdgesState are not ideal for production.

Cheers

import { useCallback } from "react";
import {
  Background,
  BackgroundVariant,
  Controls,
  type Edge,
  type Node,
  type NodeOrigin,
  ReactFlow,
  addEdge,
  useEdgesState,
  useNodesState,
  useReactFlow,
} from "@xyflow/react";

import MindMapEdge from "./mind-map-edge/mind-map-edge";
import SP123Node from "./mind-map-node/sp123-node";

const nodeTypes = {
  sp123: SP123Node,
};
const edgeTypes = {
  mindmap: MindMapEdge,
};
// this places the node origin in the center of a node
const nodeOrigin: NodeOrigin = [0.5, 0.5];
// we have to import the React Flow styles for it to work
import "@xyflow/react/dist/style.css";
import { nanoid } from "nanoid";

const initialNodes: Node[] = [];
const initialEdges: Edge[] = [];

export function MindmapForm() {
  const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
  const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
  const { screenToFlowPosition } = useReactFlow();

  const onConnect = useCallback(
    (params) => setEdges((eds) => addEdge(params, eds)),
    [setEdges]
  );

  const addSp123Node = (sp123ID: string) => {
    const id = nanoid();
    const newNode = {
      id,
      position: screenToFlowPosition({
        x: 200,
        y: 200,
      }),
      data: { sp123ID: sp123ID },
      //   origin: [0.5, 0.5],
      type: "sp123",
    };
    setNodes([...nodes, newNode]);
  };

  return (
    <div
      className="w-5/6 h-5/6"
      onPaste={(e) => {
        const pastedText = e.clipboardData.getData("text");
        addSp123Node(pastedText);
      }}
    >
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        nodeTypes={nodeTypes}
        edgeTypes={edgeTypes}
        nodeOrigin={nodeOrigin}
        onConnect={onConnect}
        fitView
      >
        <Controls showInteractive={false} />
        <Background variant={BackgroundVariant.Dots} gap={12} size={1} />
      </ReactFlow>
    </div>
  );
}
nickreynolds commented 1 month ago

thanks so much for the quick reply @hsab !

With those changes, it fixed all but 1 of the errors (and I pushed those fixes to my repo):

  const onConnect = useCallback(
    (params) => setEdges((eds) => addEdge(params, eds)),
    [setEdges]
  );

is still giving Type error: Parameter 'params' implicitly has an 'any' type. and trying to ignore it with @ts-ignore doesn't work either (changing tsconfig strict to false fixes it but that's not ideal)

I'm able to run the app, however, but when I try to load it, I get this error:

src/app/mindmap/MindmapForm.tsx (36:58) @ initialNodes
 ⨯ TypeError: (0 , _xyflow_react__WEBPACK_IMPORTED_MODULE_5__.useNodesState) is not a function or its return value is not iterable
    at MindmapForm (./src/app/mindmap/MindmapForm.tsx:36:106)
    at stringify (<anonymous>)
    at stringify (<anonymous>)
    at stringify (<anonymous>)
digest: "2303245753"
  34 |
  35 | export function MindmapForm() {
> 36 |   const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
     |                                                          ^
  37 |   const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
  38 |   const { screenToFlowPosition } = useReactFlow();

Really appreciate your help. I also found the nextjs-reactflow starter app and will try to see if I'm missing anything else that's in there. Will let you know if I'm still struggling though!

nickreynolds commented 1 month ago

Also, should the quickstart documentation be updated to include BackgroundVariant.Dots, or is that something that's only required in nextjs?

nickreynolds commented 1 month ago

I was able to resolve all my issues by starting from the xyflow/react-flow-sample-apps/reactflow-nextjs and then implementing the "add node on paste" functionality, instead of trying to start from scratch and follow different tutorials (which may be slightly outdated? or at least don't work as-is in my project)

I'll close this issue, thanks