This is generally the way WebGL works. 
WebGL is just draws into a rectangle of pixels. There is no memory of primitives. There is no structure. There is just code and the resulting canvas which is an rectangle of pixels. 
Most WebGL programs/pages clear the entire canvas every frame and redraw 100% of the things they want to show every time they draw. For tetris the general code might be something like
function render()  {
  clear the canvas
  draw the grid
  draw all the stable pieces
  draw the current piece
  draw the next piece
  draw the effects
  draw the score
}
Any knowledge of primitives or other structure is entirely up to your code. 
If you want the grid lines to be static then either set a static background with CSS or use another canvas
Using a background:
const gl = document.querySelector('#c').getContext('webgl');
function render(time) {
  time *= 0.001;
  gl.clearColor(0, 0, 0, 0);
  gl.clear(gl.COLOR_BUFFER_BIT);
  drawBlocks(gl, time);
  
  requestAnimationFrame(render);
}
requestAnimationFrame(render);
// --- below this line not important to the answer
function drawBlocks(gl, time) {
  gl.enable(gl.SCISSOR_TEST);
  
  const numBlocks = 5;
  for (let i = 0; i < numBlocks; ++i) {
    const u = i / numBlocks;
    gl.clearColor(i / 5, i / 2 % 1, i / 3 % 1, 1);
    const x = 150 + Math.sin(time + u * Math.PI * 2) * 130;
    const y = 75 + Math.cos(time + u * Math.PI * 2) * 55;
    gl.scissor(x, y, 20, 20);
    gl.clear(gl.COLOR_BUFFER_BIT);
  }
  
  gl.disable(gl.SCISSOR_TEST);
}
#c {
  background-image: url(https://i.imgur.com/ZCfccZh.png);
}
<canvas id="c"></canvas>
 
 
Using 2 canvases
// this is the context for the back canvas. It could also be webgl
// using a 2D context just to make the sample simpler
const ctx = document.querySelector('#back').getContext('2d');
drawGrid(ctx);
// this is the context for the front canvas
const gl = document.querySelector('#front').getContext('webgl');
function render(time) {
  time *= 0.001;
  gl.clearColor(0, 0, 0, 0);
  gl.clear(gl.COLOR_BUFFER_BIT);
  drawBlocks(gl, time);
  
  requestAnimationFrame(render);
}
requestAnimationFrame(render);
// --- below this line not important to the answer
function drawBlocks(gl, time) {
  gl.enable(gl.SCISSOR_TEST);
  
  const numBlocks = 5;
  for (let i = 0; i < numBlocks; ++i) {
    const u = i / numBlocks;
    gl.clearColor(i / 5, i / 2 % 1, i / 3 % 1, 1);
    const x = 150 + Math.sin(time + u * Math.PI * 2) * 130;
    const y = 75 + Math.cos(time + u * Math.PI * 2) * 55;
    gl.scissor(x, y, 20, 20);
    gl.clear(gl.COLOR_BUFFER_BIT);
  }
  
  gl.disable(gl.SCISSOR_TEST);
}
function drawGrid(ctx) {
  // draw grid
  ctx.translate(-10.5, -5.5);
  ctx.beginPath();
  for (let i = 0; i < 330; i += 20) {
    ctx.moveTo(0, i);
    ctx.lineTo(330, i);
    ctx.moveTo(i, 0);
    ctx.lineTo(i, 300);
  }
  ctx.strokeStyle = "blue";
  ctx.stroke();
}
#container {
  position: relative; /* required so we can position child elements */
}
#front {
  position: absolute;
  left: 0;
  top: 0;
}
<div id="container">
  <canvas id="back"></canvas>
  <canvas id="front"></canvas>
</div>
 
 
As for why it clears even if you didn't call clear that's because that's whqt the spec says it's supposed to do
See: Why WebGL 'clear' draw to front buffer?