3

Sorry, I can't be very specific with the details of the problem as it only happens sometimes, and I haven't been able to recreate it, which means I have no clue where to start trying to fix it.

It appears to only happen on really cheap android tablets. I have a page with a form where the user fills in details, The problem happens just after they have entered their name into a text field and then once they press onto the react-signature-canvas to start drawing their signature the app crashes (doesn't crash all the time). in the past, I think the crash was caused when the keyboard was still open when the user tried to start drawing on the signature pad.

As I said, I'm finding it really difficult to fix as I can't recreate it, so any help at all would be greatly appreciated. I'm using React Hooks and Formik.

Form:

<h2>Guardian Full Name</h2>
            <MyTextField
              label="Guardian Full Name"
              name="parentName"
              required
            />
            <ErrorMessage
              component={"div"}
              className={"termsConditionText error"}
              name={"parentSignature"}
            />

            <SignaturePad setFieldValue={setFieldValue} />

SignaturePad:

    import React, { useRef, useState } from "react";
import { Button } from "semantic-ui-react";
import "../../pages/SignDisclaimerForm/SignDisclaimerForm.css";
import "./signaturePad.css";
import SignatureCanvas from "react-signature-canvas";

export const SignaturePad = props => {
  const [canvasImageUrl, setCanvasImageUrl] = useState([
    props.parentSignature || ""
  ]);
  let sigCanvas = useRef();

  const clearCanvas = () => sigCanvas.current.clear();
  const saveCanvas = async () => {
    if (sigCanvas.current.isEmpty()) return;
    document.getElementById("parentName").blur();

    props.setFieldValue(
      "parentSignature",
      sigCanvas.current.getTrimmedCanvas().toDataURL("image/png")
    );
    setCanvasImageUrl(
      sigCanvas.current.getTrimmedCanvas().toDataURL("image/png")
    );
  };

  return (
    <div>
      {!props.disabled && (
        <div>
          <h2 style={{ marginLeft: "5%" }}>Guardian Signature</h2>
          <div className={"sigContainer"}>
            <SignatureCanvas
              ref={sigCanvas}
              canvasProps={{ className: "sigPad" }}
              onEnd={saveCanvas}
            />
          </div>
          <Button
            style={{ marginLeft: "5%", marginTop: "2%", marginRight: "2%" }}
            type={"button"}
            onClick={clearCanvas}
            children={"Clear"}
          />
          <br />
          <br />
        </div>
      )}

      {canvasImageUrl[0] && (
        <div className={"signatureDisplay"}>
          <img
            src={canvasImageUrl}
            alt={"Guardian Signature"}
            style={{ height: "100%", width: "100%" }}
          />
        </div>
      )}
    </div>
  );
};

Sentry issue report also below.

Issue Title:

TypeError HTMLCanvasElement.r(src/helpers)
error
Cannot read property 'push' of undefined

Issue Body:

../../src/helpers.ts in HTMLCanvasElement.r at line 85:17

 }
  // Attempt to invoke user-land function
  // NOTE: If you are a Sentry user, and you are seeing this stack frame, it
  //       means the sentry.javascript SDK caught an error invoking your application code. This
  //       is expected behavior and NOT indicative of a bug with sentry.javascript.
  return fn.apply(this, wrappedArguments);
  // tslint:enable:no-unsafe-any
} catch (ex) {
  ignoreNextOnError();
  withScope((scope: Scope) => {

Bread Crumbs:

Sentry BreadCrumbs

This is what the form looks like:

Form Image

Andrew Irwin
  • 691
  • 12
  • 40
  • My guess is that it is related to accessing the DOM outside of the React lifecycle, specifically around `props.disabled` and `document.getElementById`. You might consider adding a context to promote the state and let React handle the DOM state - https://kentcdodds.com/blog/how-to-use-react-context-effectively – nrako Aug 24 '20 at 15:43
  • Can you add the rest of the component class code? Or at the very least your implementation of `setFieldValue`? Anecdotally, I find most often when issues in React arise _only sometimes_, it is due to a race condition, most commonly with `setState`. – Zachary Aug 24 '20 at 18:20
  • why are you setting the value of props ? `props.setFieldValue( "parentSignature", sigCanvas.current.getTrimmedCanvas().toDataURL("image/png") );` – Nidhi Dadiya Aug 25 '20 at 04:00
  • @nrako Thank you I will check that you, to see if props.disabled is causing the issue. – Andrew Irwin Aug 25 '20 at 19:31
  • @Zachary I already have the implementation added for setFieldValue in the form snippet above. Thank you for your comment. – Andrew Irwin Aug 25 '20 at 19:33
  • 1
    @NidhiDadiya I am setting props.SetFieldValue as I am passing `setFieldValue` through from the Form component above. Thank you for your comment. – Andrew Irwin Aug 25 '20 at 19:37

2 Answers2

4

Formik author here...

You might be setting state from an unmounted DOM element (the canvas). It doesn't happen all the time because it's a race condition. You should check whether the canvas ref is actually mounted before using methods on it within your callbacks.

// ...

const sigCanvas = useRef(null);

const clearCanvas = () => {
  if (sigCanvas.current != null) {
    sigCanvas.current.clear();
  }
};

const saveCanvas = async () => {
  // Ensure that the canvas is mounted before using it
  if (sigCanvas.current != null) {
    if (sigCanvas.current.isEmpty()) return;
    document.getElementById("parentName").blur();

    props.setFieldValue(
      "parentSignature",
      sigCanvas.current.getTrimmedCanvas().toDataURL("image/png")
    );
    setCanvasImageUrl(
      sigCanvas.current.getTrimmedCanvas().toDataURL("image/png")
    );
  }
};

// ...
jaredpalmer
  • 1,791
  • 14
  • 8
  • Thank you for your answer, and thank you for writing such a fantastic library that makes my development so fantastically easily! that coupled with yup ;) I will try your answer and hopefully that will work. It has been quite frustrating to nail it down, as it only happens sometimes 10%. I will award the 50 bounty as its about to expire just for your work on Formik! I will mark the answer as accepted if it works for me. :) – Andrew Irwin Aug 25 '20 at 19:42
  • I can now recreate the crash consistently. It still appears to crash if the keyboard is still open after the user has just typed in parentName, then they straight away go to draw on the signature pad. I'll have to figure some way to disable the signature pad until the keyboard has been dismissed. – Andrew Irwin Aug 26 '20 at 07:34
0

Thank you to everyone who helped me, I really appreciated it. What I did in the end to fix the problem was just to have a green button the user had to press in order to open the signature pad. The fact that the user has to press the open button, gives the keyboard enough time to completely dismiss before the user starts to draw on the signature pad.

Thank you :)

Signature Pad with open button demo

Andrew Irwin
  • 691
  • 12
  • 40