jaredpalmer / formik

Build forms in React, without the tears 😭
https://formik.org
Apache License 2.0
33.71k stars 2.77k forks source link

React Native form resets to initial values #3951

Closed Salman9000 closed 1 month ago

Salman9000 commented 5 months ago

Form values should stay the same when executing onsubmit

Form values are being reset to empty values

import {
  Box,
  Button,
  Image,
  Input,
  Text,
  useTheme,
  // ScrollView,
  Flex
} from "native-base"
import { Formik } from "formik"
import { Ionicons } from "@expo/vector-icons"
import { useEffect, useRef, useState } from "react"
import { navigate } from "../../../rootNavigation"
import { setLoginClickedTrue } from "../../../store/loginClickedSlice"
import { useDispatch, useSelector } from "react-redux"

import { useSendVerificationEmail, useSignInEmailPassword } from "@nhost/react"
import AppVersion, { VERSION } from "../../components/AppVersion"
import LoaderModal from "../../components/LoaderModal"
import {
  View,
  Keyboard,
  ScrollView,
  Dimensions,
  StatusBar,
  TouchableWithoutFeedback
} from "react-native"
import { LinearGradient } from "expo-linear-gradient"
import { gql, useMutation } from "@apollo/client"
import AsyncStorage from "@react-native-async-storage/async-storage"
import { setSport } from "../../../store/sportSlice"
import { axiom, device } from "../../../config/util"
import { buttonColor } from "../../components/Colors"
import { TouchableOpacity } from "react-native"
import { nhost } from "../../../nhostClient"

const LoginScreen = ({ route, navigation }) => {
  const dispatch = useDispatch()
  const [loadingResetPassword, setLoadingResetPassword] = useState(false)
  const loginClicked = useSelector((state) => {
    return state.loginClicked.clicked
  })
  const { sendEmail, isLoading: loadingSendEmail } = useSendVerificationEmail()
  const {
    signInEmailPassword,
    isLoading: signInLoadng,
    needsEmailVerification,
    isError,
    error,
    isSuccess
  } = useSignInEmailPassword()
  const [togglePassword, setTogglePassword] = useState(true)
  const [signInloading, setSignInLoading] = useState(false)

  const signIn = async ({ email, password }) => {
    setSignInLoading(true)
    try {
      const res = await signInEmailPassword(
        //use nhost function to sign in
        email.toLowerCase().trim(),
        password.trim()
      )
      console.log({ res })
      if (res?.isError || !res.isSuccess) {
        //if there is error during sign in
        console.log("error in login")
        axiom.ingest("user-auth", [
          {
            version: VERSION,
            userEmail: email,
            action: "login",
            device,
            status: res?.error?.status ?? 500,
            error: res?.error ?? "Needs Email Verification"
          }
        ])
        console.log("sent axiom")
        setSignInLoading(false)
      } else {
        axiom.ingest("user-auth", [
          {
            version: VERSION,
            userEmail: email,
            action: "login",
            device,
            status: 200
          }
        ])
        navigation.navigate("DashboardDrawer")
        // getData()
      }
      setSignInLoading(false)
      return res
    } catch (e) {
      //some error in whole process
      console.log(JSON.stringify(e), "error")
    }
  }

  const handleSendVerificationEmail = async (email) => {
    console.log("click here")
    if (email) {
      try {
        const res = await sendEmail(email, {
          redirectTo: "https://tournapro.vercel.app/"
        })
        if (res?.isError) {
          axiom.ingest("logs", [
            {
              version: VERSION,
              userEmail: email,
              action: "resend verification email",
              device,
              error: res?.error,
              status: res?.error?.status ?? 500
            }
          ])
        } else {
          axiom.ingest("logs", [
            {
              version: VERSION,
              userEmail: email,
              action: "resend verification email",
              device,
              status: 200
            }
          ])
          alert(
            "Email sent successfully! Check spam folder if you cannot see the email."
          )
        }
      } catch (error) {
        axiom.ingest("logs", [
          {
            version: VERSION,
            userEmail: email,
            action: "resend verification email",
            device,
            error: error,
            status: error?.status ?? 500
          }
        ])
      }
    } else {
      alert("Enter Email Address")
    }
  }

  const handleSendResetPassword = async (email) => {
    setLoadingResetPassword(true)
    console.log("click here")
    console.log(email)
    if (email) {
      try {
        const res = await nhost.auth.resetPassword({
          email,
          options: {
            redirectTo:
              "https://tournapro.vercel.app/passwordReset"
          }
        })
        console.log(res)
        if (res?.error) {
          alert(res.error.message)
          axiom.ingest("logs", [
            {
              version: VERSION,
              userEmail: email,
              action: "Reset password",
              device,
              error: res?.error?.message,
              status: res?.error?.status ?? 500
            }
          ])
        } else {
          axiom.ingest("logs", [
            {
              version: VERSION,
              userEmail: email,
              action: "Reset password",
              device,
              status: 200
            }
          ])
          alert(
            "Password reset request sent to your email successfully. Check spam folder if you cannot see the email"
          )
        }
      } catch (error) {
        axiom.ingest("logs", [
          {
            version: VERSION,
            userEmail: email,
            action: "resend verification email",
            device,
            error: error,
            status: error?.status ?? 500
          }
        ])
      }
    } else {
      alert("Enter Email Address")
    }
    setLoadingResetPassword(false)
  }
  const { colors } = useTheme()

  const [screenHeight, setScreenHeight] = useState(
    Dimensions.get("window").height
  )
  const isFirstRef = useRef(false)
  const getScreenSize = () => {
    const statusBarHeight = StatusBar.currentHeight || 0
    const ScreenHeight = Dimensions.get("window").height
    setScreenHeight(ScreenHeight + statusBarHeight)
  }

  route.params ?? (isFirstRef.current = true)

  return (
    <TouchableWithoutFeedback
      height={screenHeight}
      onPress={() => Keyboard.dismiss()}
    >
      <Box height={screenHeight}>
        <Box bg={"white"} minW="full" flex={1} safeArea>
          <Box
            bg={"white"}
            w="full"
            justifyContent="center"
            alignItems="center"
            pt={2}
          >
            <Image
              alt="Tournapro logo"
              style={{ width: "100%", height: 180 }}
              resizeMode="contain"
              source={require("../../../assets/Login/TournaProLogo.png")}
            />
          </Box>
          <Box bg="white" flex={1} p={8} pt={1}>
            <Text fontSize={"22"} fontWeight={"500"} mb={0}>
              Login Details
            </Text>
            <Formik
              initialValues={{
                email: "",
                password: ""
              }}
              // enableReinitialize
              onSubmit={async (values) => {
                await signIn(values)
                console.log("here")
              }}
            >
              {({
                handleChange,
                handleBlur,
                handleSubmit,
                values,
                errors,
                touched
              }) => (
                <Box>
                  <Input
                    onChangeText={handleChange("email")}
                    onBlur={handleBlur("email")}
                    value={values.email}
                    variant="outline"
                    placeholder="Email"
                    autoComplete="email"
                    fontSize="sm"
                    color="black"
                    borderColor={"gray.300"}
                    borderRadius={"md"}
                    keyboardType="email-address"
                    selectionColor={colors.primary[600]}
                    _focus={{
                      borderColor: "gray.600",
                      bgColor: "white"
                    }}
                    autoCapitalize="none"
                    py={3}
                    mt={4}
                    mb={1}
                  />
                  {errors.email && touched.email && (
                    <Text color="red.500">{errors.email}</Text>
                  )}
                  <Input
                    onChangeText={handleChange("password")}
                    onBlur={handleBlur("password")}
                    value={values.password}
                    variant="outline"
                    placeholder="Password"
                    autoComplete="password"
                    fontSize="sm"
                    color="black"
                    borderColor={"gray.300"}
                    borderRadius={"md"}
                    selectionColor={colors.primary[600]}
                    type={togglePassword ? "password" : "text"}
                    _focus={{
                      borderColor: "gray.600",
                      bgColor: "white"
                    }}
                    autoCapitalize="none"
                    py={3}
                    mt={4}
                    mb={1}
                    InputRightElement={
                      <Box pr="3">
                        <Ionicons
                          onPress={() => setTogglePassword(!togglePassword)}
                          name={
                            togglePassword ? "eye-off-outline" : "eye-outline"
                          }
                          size={18}
                          color={colors.black}
                        />
                      </Box>
                    }
                  />
                  {errors.password && touched.password && (
                    <Text color="red.500">{errors.password}</Text>
                  )}

                  <Box mt={2} ml="auto">
                    <Text
                      textAlign="center"
                      bold
                      color={colors.primary[900]}
                      onPress={async () => {
                        if (!values.email) {
                          alert("Please enter your email")
                        } else {
                          console.log("meow")
                          handleSendResetPassword(values.email)
                        }
                      }}
                    >
                      Forgot Password?
                    </Text>
                  </Box>

                  <TouchableOpacity
                    onPress={() => {
                      dispatch(setLoginClickedTrue())
                      setSignInLoading(true)
                      handleSubmit()
                      Keyboard.dismiss()
                    }}
                    activeOpacity={0.8}
                    style={{
                      elevation: 2,
                      marginVertical: 6,
                      alignSelf: "center",
                      height: 53,
                      width: "100%",
                      backgroundColor: buttonColor,
                      borderRadius: 4,
                      display: "flex",
                      alignContent: "center",
                      justifyContent: "center"
                    }}
                  >
                    <Text
                      textAlign={"center"}
                      fontWeight={700}
                      fontSize={17}
                      color={"white"}
                    >
                      Login
                    </Text>
                  </TouchableOpacity>
                  {/* <Button
                    alignSelf={"center"}
                    size="lg"
                    borderRadius={4}
                    width={281}
                    height={53}
                    onPress={() => {
                      dispatch(setLoginClickedTrue())
                      setSignInLoading(true)
                      handleSubmit()
                      Keyboard.dismiss()
                    }}
                    // bgColor={"#284E8A"}
                    colorScheme={"blue"}
                    my={4}
                  >
                    <Text
                      textAlign={"center"}
                      fontWeight={700}
                      fontSize={21}
                      color={"white"}
                    >
                      Login
                    </Text>
                  </Button> */}

                  <Flex
                    direction="row"
                    justifyContent="space-around"
                    alignItems="center"
                  >
                    {/* First linear gradient */}
                    <LinearGradient
                      colors={["white", buttonColor]}
                      start={{ x: 0, y: 0 }}
                      end={{ x: 1, y: 0 }}
                      style={{ width: 90.62, height: 2.65 }}
                    />
                    <Text
                      textAlign="center"
                      underline
                      bold
                      color={colors.primary[900]}
                      onPress={() => navigate("SignUpScreen")}
                    >
                      Or Sign up
                    </Text>
                    {/* Second linear gradient */}
                    <LinearGradient
                      colors={[buttonColor, "white"]}
                      start={{ x: 0, y: 0 }}
                      end={{ x: 1, y: 0 }}
                      style={{ width: 90.62, height: 2.65 }}
                      useAngle={true}
                      angle={180}
                    />
                  </Flex>
                  {needsEmailVerification &&
                    loginClicked &&
                    !isSuccess &&
                    isFirstRef.current && (
                      <Box my={4}>
                        <Text color="red.600" bold textAlign={"center"}>
                          Verify your email address and login.
                        </Text>
                        <Text color="red.600" bold textAlign={"center"}>
                          Open your email to verify your email
                        </Text>
                        <Text color="red.600" bold textAlign={"center"}>
                          Didn't recieve verification?{" "}
                          <Text
                            onPress={() =>
                              handleSendVerificationEmail(values.email)
                            }
                            underline
                          >
                            Click here
                          </Text>{" "}
                          to send verification link again
                        </Text>
                      </Box>
                    )}
                  {needsEmailVerification &&
                    !isFirstRef.current &&
                    !isSuccess && (
                      <Box my={4}>
                        <Text color="red.600" bold textAlign={"center"}>
                          Verify your email address and login
                        </Text>
                        <Text color="red.600" bold textAlign={"center"}>
                          Open your email to verify your email
                        </Text>
                        <Text color="red.600" bold textAlign={"center"}>
                          Didnt recieve verification?{" "}
                          <Text
                            onPress={() =>
                              handleSendVerificationEmail(values.email)
                            }
                            underline
                          >
                            Click here
                          </Text>{" "}
                          to send verification link again
                        </Text>
                      </Box>
                    )}

                  {isError && (
                    <Box my={4}>
                      <Text color="red.600" bold textAlign={"center"}>
                        {error?.message}
                      </Text>
                    </Box>
                  )}
                </Box>
              )}
            </Formik>
          </Box>
        </Box>
        <AppVersion />
        <Image
          width={"100%"}
          alt="background"
          position={"absolute"}
          bottom={0}
          source={require("../../../assets/Login/Vector.png")}
        />
        <LoaderModal
          isLoading={
            signInloading ||
            loadingSendEmail ||
            signInLoadng ||
            loadingResetPassword
          }
        />
      </Box>
    </TouchableWithoutFeedback>
  )
}

export default LoginScreen
Software Version(s)
Formik 2.4.5
React 18.2.0
React native 0.71.8
npm/Yarn yarn
Operating System android/ios
erashu212 commented 1 month ago

@Salman9000 Can you create a code sandbox using https://expo.dev/

Salman9000 commented 1 month ago

@Salman9000 Can you create a code sandbox using https://expo.dev/

It seems the error isnt related to Formik, but rather the nhost sdk I am using. The nhost hooks are not compatible with react native as of yet which is causing bugs like these. I am closing this issue.