4

I have React component which renders D3 tree. The code snippet is given below.

componentDidMount()
{
    var mountNode = ReactDom.findDOMNode(this.tree);
    // Render the tree usng d3 after first component mount

    if (this.props.treeData)
    {
        renderTree(this.props.treeData, mountNode, this.props.nodeName);//renderTree is Javascript function
    }

}

contextmenu(node)
{
    this.setState = {
        style_popup: {
            top: d3.event.clientY,
            left: d3.event.clientX,
            position: 'absolute'
        },
        render_on_click: true
    }
}

render()
{
    // Render a blank svg node
    return (
        <div id="tree">
            <div id="tree-container" ref={(tree) =>
            {
                this.tree = tree;
            }}>

            </div>
            {
                (this.state.render_on_click) ? <PopUp popup_style={this.state.style_popup}/> : null
            }
        </div>
    );
}

Inside renderTree() (Not a React function) I have the following code snippet:

function renderTree(this.props.treeData, mountNode, this.props.nodeName)
{
//Some code.
.on('click',contextmenu);
}

I know this is the wrong way of calling contextmenu of React from Js, but how will I achieve this? I tried using refs

 <D3Tree ref={instance => { this.D3tree = instance; }}  treeData={this.props.treeData} />

but the D3Tree component is called from a different file which is the reason I am getting

this.D3Tree is undefined.

How should I call contextmenu which is a React function?

Roy Scheffers
  • 3,832
  • 11
  • 31
  • 36
Akash Sateesh
  • 271
  • 2
  • 4
  • 13
  • is renderTree a react component as welll? – Robbie Milejczak Jan 26 '18 at 14:36
  • No. Its a javascript function – Akash Sateesh Jan 26 '18 at 15:37
  • then what are you expecting to happen when you call contextmenu? Where is renderTree in relation to D3Tree? Unless you can bind contextmenu to D3Tree and pass down via a prop I don't think the function will do what you want it to do because it won't be able to access your component state – Robbie Milejczak Jan 26 '18 at 15:39
  • contextmenu is React function setting a state of React Component D3Tree. Now Inside renderTree function, on click, I want contextmenu to execute. But I am not able to call contextmenu as renderTree is outside React Component – Akash Sateesh Jan 26 '18 at 15:43
  • well if you have access to the component itself you can try binding it. Instead of defining the contextmenu function as a method on your component, define it as a separate function. Then inside your component you can say `this.contextmenu = contextmenu.bind(this)` and inside your renderTree you can say `contextmenu = contextmenu.bind(D3Tree)`. Otherwise your call to `this.setState` will fail. I'm not certain if that will work but it's worth trying – Robbie Milejczak Jan 26 '18 at 15:46
  • Okay Thanks. Will try it. But if I set `this.contextmenu = contextmenu.bind(this)` why do I have to set again `contextmenu = contextmenu.bind(D3Tree)` inside renderTree function ? – Akash Sateesh Jan 26 '18 at 15:49
  • it all depends on scope. I'm not certain what your setup is so I can't exactly say. If contetxmenu is bound in top-level scoped then you don't need to bind in the constructor – Robbie Milejczak Jan 26 '18 at 15:52

1 Answers1

0

In place where you have export of your component, like

let instance = null;

class MyComponent extends React.Component {
    componentWillMount() {
        instance = this;
    }
    componentWillUnmount() {
        instance = null;
    }
}

export { MyComponent, instance as myComponentInstance }

After you will able to use import { myComponentInstance } from "file" and it will be this of your component, but only if it's one rendered instance in a time, and only with null check condition.

It's also not a proper way, and your code will be hated with people who is going to support it.

Second way - you can code function in other place, out of React component Like:

const myFunction = (instance, someData) => {
}

class MyComponent extends React.Component {
    myFunction = (someData) => { myFunction(this, someData); }
}

Both way are anti-patterns, anyway, but you can achieve your goal.