简体   繁体   中英

React + D3: How can I access to the state and props of the object? (Inside a function fired by an event)a

I'm newbie in D3 and I want to create a scatterplot and when I put the mouse over a point I would like to show a text near to the point, like a tooltip.

I can change the color of the point but I cannot access to the state of React Object because I've got an error:

在此处输入图片说明

The code where previously I set the state is this:

componentDidMount(){
    let canvas = this.setCanvas();
    let scales = this.setScales(this.props.data);
    this.setState({
        canvas: canvas,
        scales: scales
    }, () => {
        this.setAxesToCanvas(canvas, scales);
        this.setPointsToCanvas(canvas, this.props.data, scales);    
    });
}

The method setCanvas returns a svg:

setCanvas(){
    // Add the visualization svg canvas to the container <div>
    let svg = d3.select("#" + this.props.idContainer)
    .append("svg")
    .style("background-color", "#354560")
    .style("color", "#FFFFFF")  //With this we've got the color of the axis too
    .attr("height", this.state.height)
    .attr("width", this.state.width);

    return svg;  
}

And setScales returns a json object:

setScales(data){ 
    let xRange = [this.state.margin.left, this.state.width - this.state.margin.right];
    let yRange = [this.state.margin.top, this.state.height - this.state.margin.top - this.state.margin.bottom]; // flip order because y-axis origin is upper LEFT  

    let xScale = d3.scaleLinear()
    .domain([ d3.min(data, d => parseFloat(d.value_x)) - 1, d3.max(data, d => parseFloat(d.value_x)) + 1])
    .range(xRange);

    let yScale = d3.scaleLinear()
    .domain([ d3.max(data, d => parseFloat(d.value_y)) + 1, d3.min(data, d => parseFloat(d.value_y)) - 1])
    .range(yRange);       

    return {"xScale" : xScale, "yScale" : yScale, "xRange" : xRange, "yRange" : yRange};
}

After setting the state I call the functions setAxesToCanvas and setPointsToCanvas. It's this last function where I have defined to catch the even "onMouseOver" how you can see:

setPointsToCanvas(canvas, data){
    let xRange = [this.state.margin.left, this.state.width - this.state.margin.right];
    let yRange = [this.state.margin.top, this.state.height - this.state.margin.top - this.state.margin.bottom]; // flip order because y-axis origin is upper LEFT  

    let xScale = d3.scaleLinear()
    .domain([ d3.min(data, d => parseFloat(d.value_x)) -1, d3.max(data, d => parseFloat(d.value_x)) + 1])
    .range(xRange);

    let yScale = d3.scaleLinear()
    .domain([ d3.max(data, d => parseFloat(d.value_y)) + 1, d3.min(data, d => parseFloat(d.value_y)) - 1])
    .range(yRange);

    canvas.selectAll("circle")
    .data(data)
    .enter().append("circle")
    .attr("class", "dot")
    .attr("r", 5.5)     //Radius size, could map to another dimension
    .attr("cx", function(d) { return xScale(parseFloat(d.value_x)); })  //x position
    .attr("cy", function(d) { return yScale(parseFloat(d.value_y)); })  //y position
    .style("fill", "#FFC107")
    .on("mouseover", this.tipMouseOver);

}

The method that I call when the event is fire is tipMouseOver and its code is:

tipMouseOver(data, iter){
    // Use D3 to select element, change color and size
    d3.select(this)
    .style("fill", "green")
    .style("radius", "5 em");

    // Specify where to put label of text
    console.log("Before to set the text - data: " + data + " iter: " + iter);
    this.state.canvas.append("text").attr({
        id: "t" + data.value_x + "-" + data.value_y + "-" + iter,  // Create an id for text so we can select it later for removing on mouseout
            x: function() { return this.state.scales.xScale(data.value_x) - 30; },
            y: function() { return this.state.scales.yScale(data.value_y) - 15; }
        })
        .text(function() {
        return [data.value_x, data.value_y];  // Value of the text
        });        
} 

How you can see in the screencap all the is executed until its arrive to the line

this.state.canvas.append("text")

If I try to pass the canvas and scales to the method which is fired when the event occurs

.on("mouseover", this.tipMouseOver(canvas, scales));

And I modify the function ...

tipMouseOver(data, iter, canvas, scales){
....
}

But when I reload the page, I've got this error:

在此处输入图片说明

I have checked that I cannot access to the props of the react object too.

Therefore, how can I get access to the state and props of the object to get canvas and scales?

Edit I:

I add the code in a codesandbox:

编辑 React + D3:如何访问对象的状态和道具? (在事件触发的函数内)

I solved what you are trying to achieve in here but not the question you asked ie, accessing state and props inside event. But I think you will find FreeCodeCamp and component function useful.

Regarding the mouse events, I moved them inside setPointsToCanvas functions, so that they can have access to canvas and scales. After that it should be straight forward. Hope this helps !

Yea you need to bind functions to "this" using .bind like in React documentation. But problems I ran into were I had a build function for my D3 chart and inside this build function I had d3 directed event listeners and handlers defined inside the build function.

I noticed inside this function if I ever tried to access this.state (react state) or update react state using this.setState it would tell me it doesn't exist. I tried using arrow functions which would still cause problems in the end. I solved my problem with the proper definition of "this" by at the top of the event listener functions setting var self = this; then I could say self.state.something and also access react setState functions by saying self.setState({something: newthing})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM