-
-
Notifications
You must be signed in to change notification settings - Fork 65
Open
Labels
Description
Hey folks, got this working in React 16 and would love to help update your docs, but not sure where to send in the PR. Here's the code I'm using (works in React 17 with TypeScript 4 strict mode).
import {
MutableRefObject,
forwardRef,
memo,
useCallback,
useEffect,
useRef,
useState,
} from "react";
import { PropsWithComponent } from "lib/types";
import { WidgetInstance } from "friendly-challenge";
interface FriendlyCaptchaOptions {
startMode: "auto" | "focus" | "none";
}
interface FriendlyCaptchaProps {
sitekey: string | null | undefined;
options?: FriendlyCaptchaOptions;
onReady?: (widget: WidgetInstance) => any;
onStarted?: (widget: WidgetInstance) => any;
onPass?: (solution: string) => any;
onFail?: (solution: string | null) => any;
onError?: (error: any) => any;
}
export const useFriendlyCaptcha = (
{
sitekey,
options = {
startMode: "none",
},
onReady,
onStarted,
onPass,
onFail,
onError,
}: FriendlyCaptchaProps,
ref: React.Ref<unknown> = null
) => {
const [widget, setWidget] = useState<WidgetInstance | null>(null);
const [solution, setSolution] = useState<string | null>(null);
const [error, setError] = useState<any>(null);
const captchaRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (ref && captchaRef) {
(ref as MutableRefObject<unknown>).current = captchaRef.current;
}
}, [captchaRef, ref]);
useEffect(() => {
if (captchaRef.current) {
captchaRef.current.setAttribute("data-sitekey", sitekey || "");
captchaRef.current.setAttribute("data-start", options.startMode || "none");
const widget = new WidgetInstance(captchaRef.current, {
doneCallback: (solution) => {
// The captcha was completed
setSolution(solution);
if (solution?.length > 0) {
onPass?.(solution);
} else {
onFail?.(solution);
}
},
errorCallback: (error) => {
// The captcha failed to load
setError(error);
onError?.(error);
},
startedCallback: () => {
// The captcha was started
onStarted?.(widget);
},
readyCallback: () => {
// The captcha is ready
onReady?.(widget);
},
});
setWidget(widget);
}
}, [onError, onFail, onPass, onReady, onStarted, options.startMode, sitekey]);
const start = useCallback(() => {
if (widget) {
widget.start();
}
}, [widget]);
const reset = useCallback(() => {
if (widget) {
widget.reset();
}
}, [widget]);
if (!sitekey || !sitekey?.length) {
console.error("FriendlyCaptcha: Missing siteKey");
setError("FriendlyCaptcha: Missing siteKey");
}
return {
start,
solution,
error,
reset,
captchaRef,
};
};
export const FriendlyCaptcha = memo(
forwardRef(
<P extends {} = {}>(
props: PropsWithComponent<FriendlyCaptchaProps> & {
componentProps?: P;
},
ref?: React.Ref<unknown>
) => {
const { component: Component = "div", componentProps = {} as P, ...captchaProps } = props;
const { captchaRef } = useFriendlyCaptcha(captchaProps, ref);
return <Component ref={captchaRef} {...componentProps} />;
}
)
);
FriendlyCaptcha.displayName = "FriendlyCaptcha";Recommend using useMemo if using the component (or someone builds their own), so adding an example like this one (for my React/Next.js build) to the docs would be good:
const captcha = useMemo(
() => (
<FriendlyCaptcha
sitekey={process.env.NEXT_PUBLIC_FRIENDLY_CAPTCHA_SITE_KEY}
options={{
startMode: "none",
}}
onPass={() => setCaptchaSuccess(true)}
onFail={() => setCaptchaSuccess(false)}
/>
),
[]
);gzuidhof