React's abstraction over the DOM means that it's not always obvious how to do DOM-related things, like working with the HTML Canvas API.
This lesson shows how the two can be combined, by creating a flexible, reusable Canvas component.
Instructor: [00:00] Let's start by creating our Canvas component. We'll keep it pretty simple for now. It will have a render method that returns a canvas element. We'll update our app component to use our new Canvas component.
[00:11] Don't be discouraged by the blank screen. Our canvas is there. It's just invisible by default. To make it easier to work with, I like to give my canvases an outline. This is temporary. We'll remove it later. That done, we can see that our canvas defaults to a size of 300x150.
[00:27] Let's give our canvas an explicit size. We could hardcode a value for width and height, but it would be better if different canvases could specify their own sizes. For that, we need to pull it out of props and pass that value down to the canvas element.
[00:39] Then we need to go back up to our app and pass explicit values. Today feels like a square day, so let's make it 200x200. As you can see, our canvas updates to reflect our new size.
[00:50] Now it's time for the fun part. In traditional JavaScript applications, you would work with the canvas by capturing a reference to it using something like document.querySelector. React lets you do this with refs. The ref attribute takes a function. React calls this function with the DOM node. Then we'll take that DOM node and assign it to the canvas instance.
[01:08] We can verify that this works inside the componentDidMount lifecycle method. For now, we'll do a quick console.log to see what this.canvas is. You'll see that it's a direct reference to the underlying canvas node, the same thing we get when we use document.querySelector.
[01:23] Earlier, I said we'd get rid of that outline that we added. Let's do that now. Let's replace it with a rectangle that we draw inside the canvas. In order to do that, we need to get a 2d context to draw in. We'll use strokeRect to draw a rectangle from the top-left corner spanning the width and height of our canvas.
[01:40] This works pretty well, but there's an improvement we can make. I don't like that this drawing instruction is hardcoded directly in the canvas. What if we want other canvases to draw other things? Instead, what if we make it so that each canvas component can specify their own drawing instructions?
[01:55] To do this, we'll give it a draw prop. The prop will be a function. That function will be supplied with a canvas and ctx. In this case, we need to move that ctx.strokeRect instruction into our new draw function. We need to make it use the canvas that we supply rather than this.canvas.
[02:11] Finally, in our canvas' componentDidMount, we have to invoke that draw method. We have to pass it the canvas and the ctx. Just like that, we've recreated the outline we had originally, except now it's being drawn by the canvas.
[02:25] This is the essence of working with Canvas in React. Our canvas component renders a canvas element. That element is given a width and a height that it takes from props. The canvas captures a reference to the underlying DOM node. It assigns that node to this.canvas. Once the component has mounted, we get a 2d context and invoke the draw method with the canvas and that new ctx.
[02:47] Up in our app, our canvas is given a width, a height, and a draw instruction. In this case, we're drawing a border, but because the draw callback is given the canvas node, the full power of the Canvas API is available to us.