Form management
Last updated
Was this helpful?
Last updated
Was this helpful?
Before we can identify the user when we receive a request, we need to create a form to collect credentials.
We will use to build our forms.
$ yarn add @types/react formik
import * as React from "react";
import { FieldProps } from "formik";
import styled from "styled-components";
import { theme } from "../../config";
const Container = styled.div``;
const StyledInput = styled.input`
border: 1px solid ${theme.colors.border};
border-radius: 4px;
padding: 8px;
font-size: 14px;
width: 100%;
box-sizing: border-box;
&:focus {
border-color: ${theme.colors.background2};
outline: none;
}
`;
const Error = styled.div`
color: red;
`;
export const Input: React.SFC<FieldProps<any>> = ({
field, // { name, value, onChange, onBlur }
form: { touched, errors }, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
...props
}) => (
<Container>
<StyledInput type="text" {...field} {...props} />
{touched[field.name] && errors[field.name] && (
<Error>{errors[field.name]}</Error>
)}
</Container>
);
import * as React from "react";
import styled from "styled-components";
import { theme } from "../../config";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCircleNotch } from "@fortawesome/free-solid-svg-icons";
import { ButtonHTMLAttributes } from "react";
export interface ButtonProps {
isLoading?: boolean;
color: keyof typeof theme.colors;
onClick?: () => void;
}
const StyledButton = styled.button<ButtonProps>`
border: 1px solid ${props => theme.colors[props.color]};
background-color: ${props => theme.colors[props.color]};
color: ${theme.colors.contrast2};
border-radius: 4px;
padding: 8px 20px;
font-size: 12px;
cursor: pointer;
width: 100%;
&:disabled {
opacity: 0.5;
}
`;
const StyledIcon = styled(FontAwesomeIcon)`
margin: 0 !important;
`;
export const Button: React.SFC<
ButtonProps & ButtonHTMLAttributes<HTMLButtonElement>
> = props => {
return (
<StyledButton {...props}>
{props.isLoading ? (
<StyledIcon spin icon={faCircleNotch} />
) : (
props.children
)}
</StyledButton>
);
};
import * as React from "react";
import { useMutation } from "react-apollo";
import { gql } from "apollo-boost";
import { Formik, Field, Form, FormikActions } from "formik";
import styled from "styled-components";
import { Input } from "../../components/Input";
import { Button } from "../../components/Button";
import { theme } from "../../config";
import { useHistory } from "react-router";
interface FormValues {
emailAddress: string;
password: string;
}
const Container = styled.div`
max-width: 400px;
margin-right: auto;
margin-left: auto;
box-shadow: 0px 0px 10px lightgrey;
padding: 30px 50px 50px;
margin-top: 10vh;
`;
const Title = styled.h1`
margin: 20px 0;
font-size: 20px;
font-weight: bold;
`;
const StyledForm = styled(Form)`
input {
margin-bottom: 20px;
}
button {
margin-top: 20px;
}
`;
const StyledErrorMessage = styled.div`
color: ${theme.colors.negative};
`;
export const SignupPage = () => {
const history = useHistory();
const [signUpMutation, { loading, error }] = useMutation(
gql`
mutation signUp($input: UserAuthInput!) {
signUp(input: $input) {
id
emailAddress
jwt
}
}
`,
{
onCompleted({ signUp }) {
localStorage.setItem("token", signUp.jwt);
history.push("/");
}
}
);
return (
<Container>
<Title>Create an account</Title>
<Formik
initialValues={{
emailAddress: "",
password: ""
}}
onSubmit={async (
values: FormValues,
{ setSubmitting }: FormikActions<FormValues>
) => {
signUpMutation({ variables: { input: values } });
setSubmitting(false);
}}
render={() => (
<StyledForm>
<Field
component={Input}
id="emailAddress"
name="emailAddress"
placeholder="Email address"
type="email"
/>
<Field
component={Input}
id="password"
name="password"
placeholder="Password"
type="password"
autoComplete="new-password"
/>
{error && (
<StyledErrorMessage>
{error.graphQLErrors[0].message}
</StyledErrorMessage>
)}
<Button isLoading={loading} color="positive" type="submit">
Submit
</Button>
</StyledForm>
)}
/>
</Container>
);
};
import React, { Suspense, lazy } from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import reset from "styled-reset";
import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";
import { LoadingScreen } from "./components/Loading";
import { Header } from "./components/Header";
import { createGlobalStyle } from "styled-components";
const SignupPage = lazy(() => import("./pages/signup"));
const LinksPage = lazy(() => import("./pages/links"));
const client = new ApolloClient({ uri: "/graphql" });
const GlobalStyle = createGlobalStyle`
${reset}
body {
font-family: "Open Sans", sans-serif;
}
a {
text-decoration: none;
color: inherit;
}
`;
const App: React.FC = () => {
return (
<ApolloProvider client={client}>
<GlobalStyle />
<div className="App">
<Router>
<Header />
<Switch>
<Route path="/signup">
<Suspense fallback={<LoadingScreen />}>
<SignupPage />
</Suspense>
</Route>
<Route path="/users">
<Suspense fallback={<LoadingScreen />}>
<LinksPage />
</Suspense>
</Route>
<Route path="/">
<div>Home</div>
</Route>
</Switch>
</Router>
</div>
</ApolloProvider>
);
};
export default App;
This cover everything needed to interact with the GraphQL API and display the results.
We need 2 components to build our form, Input
and Button
which we can then use to build our form. We send the request to the server using .
We also use and :
branch and with comments available on GitHub.