Scotty H
Scotty H

Reputation: 6694

React HTML Canvas Not Updating

I have this code in DrawingCanvas.jsx:

var _ = require('bower.js').lodash;
var React = require('bower.js').React;
var channel = require('com.js').channel;

var DrawingCanvas = React.createClass({
  setCanvasImage: function(){
    if(this.props.image != null)
    {
      //var canvas = React.findDOMNode(this.refs.canvas); // React 0.13 +
      var canvas = this.refs.canvas.getDOMNode();
      var context = canvas.getContext("2d");
      var image = new Image(100,100);
      image.src = 'data:image/png;base64,' + btoa(this.props.image);
      context.drawImage(image, 10, 10);
    }
  },
  componentWillUpdate: function(nextProps, nextState){
    this.setCanvasImage();
  },
  render: function(){
    return (
      <div>
        <canvas ref="canvas" height={this.props.height} width={this.props.width} className="drawing-canvas"></canvas>
      </div>
    );
  }
});

module.exports = DrawingCanvas;

Where I want to update the canvas when this.props.image is updated (received from server. I have verified that I receive the image from the server. Here also is my app.jsx code:

var Oboe = require('bower.js').pOboe;
var channel = require('com.js').channel;

var DrawingCanvas = require('components/DrawingCanvas.jsx');

var cfg = {
  rootApiUrl: 'http://localhost:55474/'
  //rootApiUrl: 'http://trussmgmtdevserver.azurewebsites.net/'
};

var getParameterByName = function(name) {
    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
    var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
        results = regex.exec(location.search);
    return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}

var appState = {
    image: null
}

var subscriptions = {
    image: {
        received: channel.sub('image.received', function (image) {
            appState.image = image;
            console.log("RCVD");
        })
    }
};

var renderDrawingCanvas = function (user) {
  React.render(<DrawingCanvas image={appState.image} height={90} width={90}/>, document.querySelector('.canvas-anchor'));
};

renderDrawingCanvas();

Oboe({
        url: cfg.rootApiUrl + 'api/pdfFile/edit?pdfFileId=' + getParameterByName("pdfFileId"),
        method: 'GET'
      }).then(function (data) {
        channel.pub('image.received', data.imageData);
      })
        .catch(function (err) {
          console.error(err);
        });

I receive no console errors, yet I do not see the canvas element updating. I set a breakpoint on setCanvasImage that doesn't get hit. Is componentWillUpdate (which calls setCanvasImage) not being hit for some reason? What can I do to make my canvas element update based on the updated props? Thanks in advance.

Edit:

Not a duplicate of this question. Though that question was instructive, I'm not loading an image from a url, but from base64 image data. Yes, via the Oboe request, but the problem is not that the server isn't providing the image data. It is, as stated in the question. The image data is already there, it doesn't have to be loaded. Yes, it's not there on the initial build of the React component because the Oboe request is in progress, but that comes down to when and how to call setCanvasImage which is the essence of this question. It is not a duplicate because the other question does not address this in a reactive programming context.

Upvotes: 3

Views: 6170

Answers (3)

jth41
jth41

Reputation: 3906

As suggested by Matt Huggins, try componentDidUpdate. Use the browser debugger and make sure you're dropping into your conditional inside setCanvasImage.

As others have suggested, you need some mechanism in app.jsx that will trigger DrawingCanvas props to update. This could be a high level React component, or your own control flow. The subscriptions do not have to be a React component, though that is not a bad approach. If you have more complicated sibling relationships between objects that need to be updated, your setup can be fitting. Call renderDrawingCanvas from your image.received subscription.

Upvotes: 2

compid
compid

Reputation: 1323

componentWillUpdate is not called on the initial render. Instead, it is only called when a re-render of the component is triggered with new props (that is why it's the will update lifecycle method). If you want setCanvasImage() to be called on the initial render, call it from componentDidMount. As far as I can tell, you are only rendering the component once right now.

Preferably you should perform your subscription within a container react component, and place your DrawingCanvas in that container. A subscription event could update the state in the container, and the props for Drawing Canvas could be set using the container's state. Therefore, when an image is received, the props for DrawingCanvas are updated and componentWillUpdate will be triggered.

Upvotes: 2

Georgemayer
Georgemayer

Reputation: 325

It looks to me like renderDrawingCanvas(); Is only called once. Can you re-call the method in the ajax callback? Or is it being called offscreen via the pub-sub?

Ideally, your second piece of code would be a react component as well, that would hold the image as state. Then when its state was updated, it would automatically re-render the child DrawingCanvas component. I don't have enough rep to leave this as a comment, so leaving it as an answer...

Upvotes: 2

Related Questions