Skip to content

Instantly share code, notes, and snippets.

@mwickett
Last active December 20, 2022 23:00
Show Gist options
  • Save mwickett/2aecfdeea40daa07d39e11922ae1fe20 to your computer and use it in GitHub Desktop.
Save mwickett/2aecfdeea40daa07d39e11922ae1fe20 to your computer and use it in GitHub Desktop.
Formik + Apollo
import React from 'react'
import { withRouter, Link } from 'react-router-dom'
import { graphql, compose } from 'react-apollo'
import { Formik } from 'formik'
import Yup from 'yup'
import FormWideError from '../elements/form/FormWideError'
import TextInput from '../elements/form/TextInput'
import Button from '../elements/form/Button'
import { H2 } from '../elements/text/Headings'
import Wrapper from '../elements/layout/Wrapper'
import CurrentUser from '../queries/CurrentUser'
import signinUser from '../mutations/signinUser'
const handleSubmit = (payload, { props, setSubmitting, setErrors }) => {
const {email, password} = payload
props.signinUser({ variables: { email, password } })
.then((response) => {
window.localStorage.setItem('graphcoolToken', response.data.signinUser.token)
props.data.refetch()
props.history.push('/')
}).catch((e) => {
const errors = e.graphQLErrors.map(error => error.message)
console.log(errors)
setSubmitting(false)
setErrors({ email: ' ', password: ' ', form: errors })
})
}
const LoginForm = ({
handleSubmit,
errors,
touched,
values,
handleChange,
handleBlur,
isSubmitting
}) =>
<Wrapper>
<H2>Sign In</H2>
<FormWideError error={errors.form} />
<form onSubmit={handleSubmit}>
<TextInput
id='email'
type='email'
label='Email'
placeholder='[email protected]'
error={errors.email && touched.email && errors.email}
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
/>
<TextInput
id='password'
type='password'
label='Password'
placeholder=''
error={errors.password && touched.password && errors.password}
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
/>
<Button
primary
type='submit'
disabled={
isSubmitting ||
!!(errors.email && touched.email) ||
!!(errors.password && touched.password)
}
>
Sign In
</Button>
</form>
</Wrapper>
const LoginFormWithGraphQL = compose(
graphql(signinUser, {name: 'signinUser'}),
graphql(CurrentUser, { options: { fetchPolicy: 'network-only' } }),
Formik({
validationSchema: Yup.object().shape({
email: Yup.string()
.email('Invalid email address')
.required('Email is required'),
password: Yup.string()
.required('Password is required')
}),
mapPropsToValues: ({ variables }) => ({
...variables
}),
handleSubmit: handleSubmit,
displayName: 'Login'
})
)(LoginForm)
const LoginFormWithRouter = withRouter(LoginFormWithGraphQL)
class Login extends React.Component {
componentWillUpdate (nextProps) {
if (!this.props.data.user && nextProps.data.user) {
this.props.history.push('/dashboard')
}
}
render () {
if (this.props.data.loading) {
return (<div></div>)
}
return (
<div>
<LoginFormWithRouter variables={{ email: '', password: '' }} />
<p>Don't have an account? <Link to='/signup'>Create one now</Link></p>
</div>
)
}
}
// Set network fetch policy to network only for security reasons
export default graphql(CurrentUser, { options: { fetchPolicy: 'network-only' } })(withRouter(Login))
@churcho
Copy link

churcho commented Oct 20, 2018

I found the solution, by adding an update function right after variables in the mutation.

.mutate({
      variables: { email, password },

      update: (cache, { data: { token } }) => {
        ...
      },
    })

@Pruxis
Copy link

Pruxis commented Dec 8, 2018

use withFormik instead of Formik if you are using the latest version of Formik as of now

@benbowler
Copy link

@gajus
Copy link

gajus commented Mar 8, 2019

@benbowler, in your example, how do you reset setSubmitting in case of an error?

This is the code that I have at the moment:

// @flow

import React, {
  useState
} from 'react';
import Textarea from 'react-textarea-autosize';
import gql from 'graphql-tag';
import {
  Query,
  Mutation
} from 'react-apollo';
import Modal from 'react-modal';
import {
  Formik,
  Field
} from 'formik';
import {
  createStyleName
} from '../factories';
import {
  CreateProbeMutation
} from '../mutations';
import styles from './ProbeListView.module.scss';
import ProbeMonitorCard from './../components/ProbeMonitorCard';
import PrimaryButton from './../components/PrimaryButton';
import FormField from './../components/FormField';
import InlineError from './../components/InlineError';

const styleName = createStyleName(styles);

const CreateProbeForm = (props) => {
  console.log('CreateProbeForm.props', props);

  return <form className={styleName('form-body')} onSubmit={props.handleSubmit}>
    <Field
      label='Description'
      type='textarea'
      name='description'
      description='Describe what the probe is checking, e.g. "There must be no queries running longer than 30 seconds." You can use bold, italic and inline-code markdown in the description.'
      component={FormField}
    />

    <Field
      label='Query'
      type='textarea'
      name='query'
      description='SQL query. Each row returned from the query is interpreted as a warning. Under normal working conditions, query must return no results.'
      component={FormField}
    />

    <div className={styleName('form-field button-group')}>
      <PrimaryButton
        type='submit'
        inProgress={props.isSubmitting}
      >Create Probe</PrimaryButton>
    </div>
  </form>;
};

export default () => {
  const [createProbeIsOpen, setCreateProbeIsOpen] = useState(false);
  const [queryFilter, setQueryFilter] = useState('');

  return <>
    <div className={styleName('query-filter')}>
      <input
        id='query-filter'
        placeholder='Search for a query'
        type='text'
        className={styleName('text-input')}
        onChange={(event) => {
          setQueryFilter(event.target.value);
        }}
      />
    </div>

    <Modal
      isOpen={createProbeIsOpen}
      onRequestClose={() => {
        setCreateProbeIsOpen(false);
      }}
      className={styleName('modal')}
      overlayClassName={styleName('overlay')}
      >
      <div className={styleName('modal-header')}>
        <div className={styleName('name')}>Create probe</div>
      </div>
      <div className={styleName('modal-body')}>
        <Mutation
          onCompleted={() => {
            this.props.history.push('/');
          }}
          mutation={CreateProbeMutation}>
          {(createProbe, {loading, error, data}) => {
            let errorElement;

            if (error) {
              

              console.log('error', error);

              errorElement = <InlineError
                error={error}
              />;
            }

            return <>
              {errorElement}
              <Formik
                initialValues={{
                  description: '',
                  query: ''
                }}
                onSubmit={(values) => {
                  createProbe({
                    variables: {
                      databaseConnectionId: 1,
                      description: values.description,
                      query: values.query,
                      executionInterval: 60,
                      executionTimeout: 60,
                      labelIds: []
                    }
                  })
                }}
                render={CreateProbeForm}
              />
            </>;
          }}
        </Mutation>
      </div>
    </Modal>

    <Query
        query={gql`
          {
            probes(first : 10) {
              edges {
                node {
                  id
                  description
                }
              }
            }
          }
        `}
      >{(props) => {
        if (props.loading) {
          return <div>Loading</div>;
        }

        const probeListItems = props.data.probes.edges
          .filter((edge) => {
            return edge.node.description.toLowerCase().includes(queryFilter.toLowerCase());
          })
          .map(({node}) => {
            return <li key={node.id}>
              <ProbeMonitorCard
                {...node}
              />
            </li>;
          });

        return <div>
          <ol className={styleName('probe-monitor-list')}>
            {probeListItems}
          </ol>
        </div>;
      }}</Query>

    <div className={styleName('navigation')}>
      <PrimaryButton onClick={() => { setCreateProbeIsOpen(true); }}>
        Create probe
      </PrimaryButton>
    </div>
  </>;
};

@gajus
Copy link

gajus commented Mar 8, 2019

I could just do this:

onSubmit={(values, {setSubmitting}) => {
  createProbe({
    variables: {
      databaseConnectionId: 1,
      description: values.description,
      query: values.query,
      executionInterval: 60,
      executionTimeout: 60,
      labelIds: []
    }
  })
  .catch((error) => {
    setSubmitting(false);
  })
}}

Not sure if thats the way to go, though.

@rigalpatel001
Copy link

Use the below code example. It works fine.

import React,{ useState } from "react";
import { StyleSheet, Image ,Text } from "react-native";
import * as Yup from "yup";
import { gql, useQuery,useMutation } from '@apollo/client';

import Screen from "../components/Screen";
import { ErrorMessage,Form, FormField, SubmitButton } from "../components/forms";
import ActivityIndicator from "../components/ActivityIndicator";
import useAuth from "../auth/useAuth";


const validationSchema = Yup.object().shape({
  email: Yup.string().required().email().label("Email"),
  password: Yup.string().required().min(4).label("Password"),
});

const LOG_IN = gql`
mutation SignInMutation($signInLogin: String!, $signInPassword: String!) {
  signIn(login: $signInLogin, password: $signInPassword) {
    token
  }
}
`;
function LoginScreen(props) {

  const auth = useAuth();
  const [loginFailed, setLoginFailed] = useState(false);
  const [isloading, setIsloading] = useState(false);

  const handleSubmit = async ({ email, password }) => {
    setIsloading(true);
   try {
    const result = await signIn({ variables: { signInLogin: email,signInPassword:password } });
    setIsloading(false);
    auth.logIn(result.data.signIn.token);
   } catch (error) {
    console.log("Error", error);
    setLoginFailed(true);
    setIsloading(false);
   }
  };

   const [signIn, { data, loading, error }] = useMutation(LOG_IN);
   //if (loading) return <Text>Submitting...</Text>
   //if (error) setLoginFailed(true);

  return (
    <>
    <ActivityIndicator visible={isloading} />
    <Screen style={styles.container}>
      <Image style={styles.logo} source={require("../assets/logo.png")} />
      <Form
        initialValues={{ email: "", password: "" }}
        onSubmit={handleSubmit}
        validationSchema={validationSchema}
      >
         <ErrorMessage
          error="Invalid email and/or password."
          visible={loginFailed}
        />
        <FormField
          autoCapitalize="none"
          autoCorrect={false}
          icon="email"
          keyboardType="email-address"
          name="email"
          placeholder="Email"
          textContentType="emailAddress"
        />
        <FormField
          autoCapitalize="none"
          autoCorrect={false}
          icon="lock"
          name="password"
          placeholder="Password"
          secureTextEntry
          textContentType="password"
        />
        <SubmitButton title="Login" />
      </Form>
    </Screen>
    </>
  );
}

const styles = StyleSheet.create({
  container: {
    padding: 10,
  },
  logo: {
    width: 200,
    height: 80,
    alignSelf: "center",
    marginTop: 50,
    marginBottom: 20,
  },
});

export default LoginScreen;

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