首页 > 解决方案 > 使用 SVG Rect On Click 处理程序更改 React 状态

问题描述

通常,我确保为我的问题包含一个代码示例,但是在这种情况下,我的代码与以下 D3 单选按钮示例100% 相似,我只是试图将其包含在我的反应组件中。

示例中的相关代码是点击处理程序:

.on("click",function(d,i) {
  updateButtonColors(d3.select(this), d3.select(this.parentNode))
  d3.select("#numberToggle").text(i+1)
});

但是,当单击此单选按钮时,我试图更改我的反应应用程序的状态,而不是切换一个数字。现在,假设我只是想将状态设置为 1、2 或 3 之一,这样(i + 1)就是我想要设置的状态。

我尝试在这里的点击处理程序中直接调用 setState() ,但是我的状态没有改变。关于我如何做到这一点的任何想法?让我知道这里是否需要更多我的代码。

编辑:我已经尝试添加到目前为止的内容片段,但我正在努力让它在 stackoverflow 上工作。

class App extends React.Component {
  constructor(props) { 
    super(props);
    this.state = { 
      chartType: 1
    }
  }
  
  drawChartTypeButton() {
    
    // colors for different button states 
    const defaultColor= "#7777BB"
    const hoverColor= "#0000ff"
    const pressedColor= "#000077"
    const bWidth= 8; //button width
    const bHeight= 5; //button height
    const bSpace= 1; //space between buttons
    const x0 = 5; //x offset
    const y0 = 5; //y offset
    const labels = [1, 2, 3];
    const updateButtonColors = function(button, parent) {
      parent.selectAll("rect")
        .attr("fill",defaultColor)

      button.select("rect")
        .attr("fill",pressedColor)
    }
    
    // groups for each button (which will hold a rect and text)
    const chartTypeButton = d3.select('g.allbuttons')    
    const buttonGroups= chartTypeButton.selectAll("g.button")
      .data(labels)
      .enter()
      .append("g")
      .attr("class", "button")
      .style("cursor", "pointer")
      .on("click", function(d,i) {
        updateButtonColors(d3.select(this), d3.select(this.parentNode))
        this.setState({chartType: 2})
      })
      .on("mouseover", function() {
        if (d3.select(this).select("rect").attr("fill") != pressedColor) {
          d3.select(this)
            .select("rect")
            .attr("fill",hoverColor);
        }
      })
      .on("mouseout", function() {
        if (d3.select(this).select("rect").attr("fill") != pressedColor) {
          d3.select(this)
            .select("rect")
            .attr("fill",defaultColor);
        }
      })
    
    buttonGroups.append("rect")
      .attr("class","buttonRect")
      .attr("width",bWidth)
      .attr("height",bHeight)
      .attr("x", function(d,i) {return x0+(bWidth+bSpace)*i;})
      .attr("y",y0)
      .attr("rx",1) //rx and ry give the buttons rounded corners
      .attr("ry",1)
      .attr("fill",defaultColor)
    
    // adding text to each toggle button group, centered 
    // within the toggle button rect
    buttonGroups.append("text")
      .attr("class","buttonText")
      .attr("font-family", "arial")
      .attr("font-size", "0.1em")
      .attr("x",function(d,i) {
        return x0 + (bWidth+bSpace)*i + bWidth/2;
      })
      .attr("y",y0)
      .attr("text-anchor","middle")
      .attr("dominant-baseline","central")
      .attr("fill","black")
      .text(function(d) {return d;})
  }
  
  componentDidMount() {
    
    const chart = d3.select('.chart')
      .attr('width', 320)
      .attr('height', 240)
      .attr("viewBox", "0, 0, " + 50 + ", " + 50 + "")
      
    this.drawChartTypeButton();
    
  }
  
  render() {
    return(
      <div className='container'>
        <svg className='chart'>
          <g className="allbuttons" />
        </svg>
      </div>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

<div id='root'>
  Damnit Work
</div>

标签: javascriptreactjsd3.js

解决方案


您似乎this在点击处理程序内部混淆了范围,您都将 this 用于选择器和 react 组件。

通常我们可以使用箭头函数来保留this范围,但是您似乎也需要它来用于 d3,只需创建一个保存当前上下文的局部变量,这样您就可以在 click 函数中重用它

// create a local reference to "this" in the drawCharTypeButton function
const self = this; 

// use the local reference to update the componenents state
.on("click", function(d,i) {
    updateButtonColors(d3.select(this), d3.select(this.parentNode));
    self.setState({chartType: 2});
})

然后您当前的代码将正常工作(真的,它只显示 3 个按钮,并选择 3 个中的任何一个)

请注意,在您的示例代码中,chartWidthandchartHeight变量未定义,因此我将它们设置为 320x240,因此它与 SO 上的渲染空间有点匹配

class App extends React.Component {
  constructor(props) { 
    super(props);
    this.state = { 
      chartType: 1
    }
  }
  
  drawChartTypeButton() {
    
    // colors for different button states 
    const defaultColor= "#7777BB"
    const hoverColor= "#0000ff"
    const pressedColor= "#000077"
    const bWidth= 8; //button width
    const bHeight= 6; //button height
    const bSpace= 0.5; //space between buttons
    const x0 = 5; //x offset
    const y0 = 14; //y offset
    const labels = [1, 2, 3];
    const updateButtonColors = function(button, parent) {
      parent.selectAll("rect")
        .attr("fill",defaultColor)

      button.select("rect")
        .attr("fill",pressedColor)
    }
    
    // groups for each button (which will hold a rect and text)
    const self = this;
    const chartTypeButton = d3.select('g.allbuttons')    
    const buttonGroups= chartTypeButton.selectAll("g.button")
      .data(labels)
      .enter()
      .append("g")
      .attr("class", "button")
      .style("cursor", "pointer")
      .on("click", function(d,i) {
        updateButtonColors(d3.select(this), d3.select(this.parentNode))
        self.setState({chartType: 2})
      })
      .on("mouseover", function() {
        if (d3.select(this).select("rect").attr("fill") != pressedColor) {
          d3.select(this)
            .select("rect")
            .attr("fill",hoverColor);
        }
      })
      .on("mouseout", function() {
        if (d3.select(this).select("rect").attr("fill") != pressedColor) {
          d3.select(this)
            .select("rect")
            .attr("fill",defaultColor);
        }
      })
    
    buttonGroups.append("rect")
      .attr("class","buttonRect")
      .attr("width",bWidth)
      .attr("height",bHeight)
      .attr("x", function(d,i) {return x0+(bWidth+bSpace)*i;})
      .attr("y",y0)
      .attr("rx",5) //rx and ry give the buttons rounded corners
      .attr("ry",5)
      .attr("fill",defaultColor)
    
    // adding text to each toggle button group, centered 
    // within the toggle button rect
    buttonGroups.append("text")
      .attr("class","buttonText")
      .attr("font-family", "arial")
      .attr("font-size", "0.1em")
      .attr("x",function(d,i) {
        return x0 + (bWidth+bSpace)*i + bWidth/2;
      })
      .attr("y",y0+bHeight/2)
      .attr("text-anchor","middle")
      .attr("dominant-baseline","central")
      .attr("fill","white")
      .text(function(d) {return d;})
  }
  
  componentDidMount() {
    
    const chart = d3.select('.chart')
      .attr('width', 160)
      .attr('height', 120)
      .attr("viewBox", "0, 0, " + 50 + ", " + 50 + "")
      
    this.drawChartTypeButton();
    
  }
  
  render() {
    return(
      <div className='container'>
        <svg className='chart'>
          <g className="allbuttons" />
        </svg>
      </div>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

<div id='root'>
  Damnit Work
</div>

关于、最佳实践组合的挑剔,您应该尝试在内部进行所有 DOM 操作。

现在对于一个可能不完全可能的图表,但是这 3 个按钮可以很容易地呈现而不需要

我还没有组合这些渲染引擎,所以我不能说你目前的方法是否有缺点


推荐阅读