# Form management

Before we can identify the user when we receive a request, we need to create a form to collect credentials.

![](/files/-Lqg20Eoicu8tRJ7c3Qv)

We will use [Formik](https://jaredpalmer.com/formik/) to build our forms.

```
$ yarn add @types/react formik
```

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 [`useMutation`](https://www.apollographql.com/docs/tutorial/mutations/#update-data-with-usemutation).

{% tabs %}
{% tab title="components/Input.ts" %}

```typescript
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>
);
```

{% endtab %}

{% tab title="components/Button.tsx" %}

```typescript
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>
  );
};
```

{% endtab %}

{% tab title="pages/SignupPage.tsx" %}

```typescript
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>
  );
};
```

{% endtab %}
{% endtabs %}

We also use [React Router](https://reacttraining.com/react-router/) and [Lazy](https://reactjs.org/docs/code-splitting.html):

{% code title="App.tsx" %}

```typescript
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;
```

{% endcode %}

This cover everything needed to interact with the GraphQL API and display the results.

{% hint style="info" %}
[`form`](https://github.com/florianherrengt/book-code/tree/form) branch and [pull request](https://github.com/florianherrengt/book-code/pull/3) with comments available on GitHub.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tutorial.specian.co.uk/frontend/form-management.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
