最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Best way to retrieve and cache bounding boxes of SVG nodes with React.JS - Stack Overflow

programmeradmin5浏览0评论

I am rendering a SVG ponent with labels. Those label ponents need to be correctly laid out depending on their text content (and so their size) in order to avoid overlapping themselves.

To get the real size of each labels, it seems a double-render is needed each time a label content is updated.

At the label ponent level, I need to

  • render it for the first time
  • retrieve the bounding box of the real SVG DOM node
  • cache the bounding box for performance reason
  • re-render the ponent to adjust the label position according to its cached bounding box

Then, at each redraw :

  • render according to the cached bounding box
  • pare label content between previous and updated props and if changed:
  • update and cache the label bounding box
  • re-render according to the updated and cached bounding box

So far, here is how I have implemented my label ponent:

var Label = React.createClass({

  updateBBox: function() {
    // Trigger re-rendering
    this.setState({
      bbox: this.getDOMNode().getBBox()
    });
  },

  ponentDidMount: function() {
    // Will trigger a re-rendering at mount
    this.updateBBox();
  },

  ponentDidUpdate: function(prevProps, prevState) {
    // If content has changed, re-render
    if (this.props.content !== prevProps.content) {
      this.updateBBox();
    }
  },

  render: function() {
    // Render according to size from current bounding box if any cached
    // ...
  }

});

So, my question is not about the algorithm but if there is a better React-pliant way to implement this. Is using the state for caching this sort of slow 'putations' or DOM accesses allowed in the React way? Is double-rendering an unusual case for React?

I am rendering a SVG ponent with labels. Those label ponents need to be correctly laid out depending on their text content (and so their size) in order to avoid overlapping themselves.

To get the real size of each labels, it seems a double-render is needed each time a label content is updated.

At the label ponent level, I need to

  • render it for the first time
  • retrieve the bounding box of the real SVG DOM node
  • cache the bounding box for performance reason
  • re-render the ponent to adjust the label position according to its cached bounding box

Then, at each redraw :

  • render according to the cached bounding box
  • pare label content between previous and updated props and if changed:
  • update and cache the label bounding box
  • re-render according to the updated and cached bounding box

So far, here is how I have implemented my label ponent:

var Label = React.createClass({

  updateBBox: function() {
    // Trigger re-rendering
    this.setState({
      bbox: this.getDOMNode().getBBox()
    });
  },

  ponentDidMount: function() {
    // Will trigger a re-rendering at mount
    this.updateBBox();
  },

  ponentDidUpdate: function(prevProps, prevState) {
    // If content has changed, re-render
    if (this.props.content !== prevProps.content) {
      this.updateBBox();
    }
  },

  render: function() {
    // Render according to size from current bounding box if any cached
    // ...
  }

});

So, my question is not about the algorithm but if there is a better React-pliant way to implement this. Is using the state for caching this sort of slow 'putations' or DOM accesses allowed in the React way? Is double-rendering an unusual case for React?

Share Improve this question edited Oct 7, 2014 at 11:10 gentooboontoo asked Oct 7, 2014 at 10:47 gentooboontoogentooboontoo 4,8533 gold badges22 silver badges15 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 7

My personal opinion is that you should put all state that matters to you into the state of a ponent. So yes, the way you propose is the right way to do it.

As far as I know, double renders are the only way to get the size of a block as state, since the block needs to be drawn first. Since React is so performant, this is usually no problem. if it is, you use PureRenderMixin or other checks on shouldComponentUpdate within the children to ensure performance.

Note however that it might be smart to write this as a mixin, because of "don't repeat yourself".

I do responsive SVG stuff quite frequently so I often need x, y width and height from a block element (svg or html).

I've written this little mixin called BoundingRectAware:

var shallowEqual = require('react/lib/shallowEqual');

// keep width and height of an element. You must make a "boundingRectTarget" ref
// to the element you would like to track
module.exports = {
    getInitialState: function() {
        return {rect: {
            left: null, top: null, right: null, bottom: null, width: null, height: null
        }};
    },
    ponentDidMount: function() {
        window.addEventListener("resize", this.updateDimensions);
        this.updateDimensions();
    },
    ponentWillUnmount: function() {
        window.removeEventListener("resize", this.updateDimensions);
    },
    ponentWillReceiveProps: function() {
        this.updateDimensions();
    },
    updateDimensions: function() {
        if (this.refs.boundingRectTarget) {
            var rect = this.refs.boundingRectTarget.getDOMNode().getBoundingClientRect();
            if (!shallowEqual(this.state.rect, rect)) {
                this.setState({rect: rect});
            }
        }
    }
};

this can be used like so:

var FooComponent = React.createClass({
    mixins: [BoundingRectAware],
    render: function() {
        return <div className="foo-class" ref="boundingRectTarget"/>;
    }
});

within FooComponent, you will automatically get the boundingClientRect as state. I would implement something alike if you use getBBox() in more than one place.

发布评论

评论列表(0)

  1. 暂无评论