React组件中的Uncaught TypeError: ***不是一个函数

9
我将一个对象作为参数传递给React组件的构造函数。然后,我想调用对象中的一个函数,但是出现了以下错误信息:
Uncaught TypeError: _this.layout.getWbsLayout is not a function
    at new Wbs (Wbs.tsx:50)
    at react-dom.js:4749
    at measureLifeCyclePerf (react-dom.js:4529)
    at ReactCompositeComponentWrapper._constructComponentWithoutOwner (react-dom.js:4748)
    at ReactCompositeComponentWrapper._constructComponent (react-dom.js:4734)
    at ReactCompositeComponentWrapper.mountComponent (react-dom.js:4642)
    at Object.mountComponent (react-dom.js:11542)
    at ReactCompositeComponentWrapper.performInitialMount (react-dom.js:4825)
    at ReactCompositeComponentWrapper.mountComponent (react-dom.js:4712)
    at Object.mountComponent (react-dom.js:11542)

我阅读了一些有关在构造函数中将方法绑定到this的帖子,但由于我调用的是作为参数传递的对象的函数,因此我不确定此处是否适用,并且仍然收到了类似的消息,即bind不是一个函数

这是我的代码,Wbs是根React组件,WbsLayout是我作为参数传递给Wbs构造函数的对象。

错误发生在构造函数中,当我执行this.layout.getWbsLayout()时。请注意,我尝试在ComponentDidMount中调用该函数,但是仍然出现了相同的错误。

import * as React from 'react';
import WbsLayout from "../layout/WbsLayout";

interface WbsProps {}
interface WbsState {
    wbsElements: any[];
    wbsEdges: any[]
}

class Wbs extends React.Component<WbsProps, WbsState>{

    public cy: Cy.Instance;
    private layout: WbsLayout;
    private s: snap.Paper;
    private renderer: WbsRenderer;

    public state : WbsState;

    constructor(props: WbsProps, layout: WbsLayout) {
        super(props);
        this.layout = layout;
        this.cy = this.layout.cy;

        // this does show a json object in the console, hence layout can be accessed
        console.log(layout.cy.elements().jsons());

        this.layout.getWbsLayout(); //The error happens here

        // initial state
        this.state = {
            wbsElements: [],
            wbsEdges: [],
        };
    }

    public render() {

        const wbsElements: any = this.state.wbsElements.map((element: any) => (
            <WbsElement
                key={element.wbsElementBox.nodeId}
                wbsElementBox={...element.wbsElementBox}
                wbsElementTitle={...element.wbsElementTitle}
                wbsElementId={...element.wbsElementId}
            />
        ));

        const wbsEdges: any = this.state.wbsEdges.map((edge: any) => (
            <WbsEdge
                key={edge.edgeId}
                edgeId={edge.edgeId}
                sourceWbsElementData={...edge.sourceWbsElementData}
                targetWbsElementData={...edge.targetWbsElementData}
            />
        ));

        return (
            <svg>
                {wbsElements}
                {wbsEdges}
            </svg>
        );
    }
}

export default Wbs;

这里介绍一下WbsLayout

导出WbsLayout类:

    public cy: Cy.Instance;

    constructor(graph: any, Options?: Options) {

        this.cy = cytoscape({
            headless: true,
            elements: graph
        });

    }

    public getWbsLayout(): any {
        const wbsElements: any = this.cy.collection("node[visibility = 'visible']").map((n: Cy.CollectionNodes) =>
            ({
                wbsElementBox: {
                    nodeId: n.data().id,
                    x: n.data().x,
                    y: n.data().y,
                    width: n.data().width,
                    height: n.data().height
                },
                wbsElementTitle: {
                    text: n.data().title
                },
                wbsElementId: {
                    text: n.data().wbsId
                }
            })
        );

        const wbsEdges: any = [
            {
                edgeId: '5',
                sourceWbsElementData: {
                    nodeId: '1',
                    x:464.359375,
                    y:30,
                    width:100,
                    height:40,
                    layoutStyle: 0
                },
                targetWbsElementData: {
                    nodeId:'1.5',
                    x:867.875,
                    y:100,
                    width:130.84375,
                    height:40,
                    layoutStyle: 1
                }

            }
        ];

        return {
            wbsElements,
            wbsEdges
        };
    }

    ....
}
export default WbsLayout;

这里是我如何实例化Wbs的方法:
let graph: any = {
    nodes: [],
    edges: []
};

let layout: WbsLayout = new WbsLayout(graph);
let wbs = new Wbs(null, layout);

我可以在Wbs的构造函数中看到json对象: console.log(layout.cy.elements().jsons());

建议您在渲染时执行 wbsElementswbsEdges 函数。 - Andrew Li
可能是因为 React.createElement 在实例化您的组件时,没有将 WbsLayout 实例传递给您的构造函数。 - Andrew Li
你是如何将 layout 传递给构造函数的?在 React 中,如果你想要传递一些东西,你可以使用 props - Nitzan Tomer
我刚刚编辑了我的帖子来回答你的问题。当我实例化时,我确实将布局传递给了Wbs,并且如果我在Wbs的构造函数中运行console.log(...),我可以访问它。 - Greg Forel
2个回答

1

编辑:问题最终与此无关,但值得记住的是,React非常擅长在组件的“测试”过程中提前发出警告,并在props未按预期接收时抛出错误或警告。这些早期警告有时非常“有用”,以至于它们可能会干扰您正常的调试本能。


原始答案:

要么在传递props之前屏蔽函数调用,要么设置默认的props,设置某种虚拟或回退函数行为。

在构造函数中:

if (this.layout.getWbsLayout) {
  this.layout.getWbsLayout();
}

其他选项:

  • 为函数定义默认值。
  • 根据需要在componentWillMountcomponentDidMount中执行函数。生命周期钩子是执行数据实例化的正确位置。构造函数最适合状态和静态调用,例如绑定类函数。

我尝试在componentDidMount里面调用,但是未成功,错误信息相同:componentDidMount() { let layout: any = this.layout.getWbsLayout(); this.setState( { wbsElements: layout.wbsElements, wbsEdges: layout.wbsEdges } ); } - Greg Forel
你尝试过使用 if 方法吗 -- 甚至可以通过查看 typeof 来使测试更具体。我不认为这是长期的方法,但它会帮助你缩小问题的根本原因。如果你至少在这里将代码精简到最小限度以重现问题,那也会有所帮助。这里有很多代码,但似乎只有5-10行与问题相关,但也许还有其他我没有注意到的东西。 - ballenf
感谢您的帮助,本。基本上,尽管我能够通过console.log输出布局,但它并未被传递,就像Nitzan和Andrew在上面提到的那样。我进行了一些重构,并通过props传递了布局,现在它运行得非常好。顺便说一下,按照您的建议,在componentWillMount中执行我的数据实例化。 - Greg Forel
@GregForel 很高兴听到这个消息!我们都曾经历过这种情况。你会认为问题与你正在做的新事物有关...至少对我来说是这样! - ballenf

0
我遇到了相同的错误文本,看起来这个问题是第一个出现的问题,所以我会在这里写下我的解决方案。
我有这段代码:
 onClick={this.setSelected(item)}

并且不得不将其更改为这样:
 onClick={() => this.setSelected(item)}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接