Skip to content

Instantly share code, notes, and snippets.

@andrelandgraf
Last active December 28, 2021 18:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrelandgraf/2434bc466ef62d870db0ce6264a709a8 to your computer and use it in GitHub Desktop.
Save andrelandgraf/2434bc466ef62d870db0ce6264a709a8 to your computer and use it in GitHub Desktop.
working with URLSearchParams in remix.run
import { Form as RemixForm } from 'remix';
import type { FC } from 'react';
import type { FormProps, Request } from 'remix';
type FormReqBody = { method: 'get' | 'post' | 'put' | 'delete' } & Record<string, string>;
export async function parseURLSearchParams(request: Request): Promise<Partial<FormReqBody>>;
export async function parseURLSearchParams<ReqBody>(request: Request): Promise<Partial<ReqBody & FormReqBody>>;
export async function parseURLSearchParams<ReqBody>(request: Request): Promise<Partial<ReqBody & FormReqBody>> {
const queryString = await request.text();
const searchParams = new URLSearchParams(queryString);
const body: any = {};
const entries = Array.from(searchParams.entries());
entries.forEach(([key, value]) => {
body[key] = value;
});
body.method = (searchParams.get('_method') ?? request.method).toLowerCase();
return body as Partial<ReqBody & FormReqBody>;
}
const Form: FC<FormProps> = ({ children, method, ...props }) => {
const notAllowedMethod = method !== 'get' && method !== 'post';
return (
<RemixForm
{...props}
// only 'get' and 'post' are allowed for html form
method={notAllowedMethod ? 'post' : method}
>
{
// need this hidden input because regular forms don't all for the delete method https://docs.remix.run/v0.17/api/remix/#form-method
notAllowedMethod ? <input type="hidden" name="_method" value={method} /> : null
}
{children}
</RemixForm>
);
};
export default Form;
interface LoginForm {
email: string;
password: string;
}
const getErrorRoute = (errCode: AuthErrors) => `/auth/login?error=${errCode}`;
export const action: ActionFunction = async ({ request }) => {
const { method, email, password } = await parseURLSearchParams<LoginForm>(request);
if (method !== 'post') {
return redirect(getErrorRoute(AuthErrors.internalError));
}
const [status, state, resData] = await auth.loginWithPassword(email, password);
if (status !== 200 || !resData) {
switch (state) {
case auth.LoginResStatus.emailRequired:
case auth.LoginResStatus.passwordRequired:
return redirect(getErrorRoute(AuthErrors.informationMissing));
case auth.LoginResStatus.userNotFound:
return redirect(getErrorRoute(AuthErrors.userNotFound));
case auth.LoginResStatus.wrongPassword:
return redirect(getErrorRoute(AuthErrors.wrongPassword));
default:
return redirect(getErrorRoute(AuthErrors.internalError));
}
}
return createUserSession(request, resData.token.accessToken, '/dashboard/projects');
};
export default function Login() {
const formSubmit = usePendingFormSubmit();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
<CenteredContent>
<h1>Sign in</h1>
<FormContainer>
<Form method="post">
<Input
type="email"
id="email"
name="email"
autoComplete="username"
value={email}
handleChange={(v) => setEmail(v)}
label="Email"
required
/>
<Password
id="password"
name="password"
autoComplete="current-password"
value={password}
handleChange={(v) => setPassword(v)}
label="Password"
required
/>
<Button type="submit" isLoading={!!formSubmit} primary>
Login
</Button>
</Form>
</FormContainer>
</CenteredContent>
);
}
@andrelandgraf
Copy link
Author

Note: This Form component is not needed anymore. The Remix Form component now provides that functionality built-in!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment