zaguiini / formik-wizard

A multi-step form component powered by formik and react-albus
MIT License
86 stars 26 forks source link

Accessing form values from the children #23

Closed ataravati closed 4 years ago

ataravati commented 4 years ago

I'm using formik-wizard with React Native and Typescript. I use useFormikContext() to access the values from inside each step, but the problem is the values are only limited to those of that particular step. How do I access the entire form values?

zaguiini commented 4 years ago

const { values } = useFormikWizardContext() has your values.

ataravati commented 4 years ago

@zaguiini I'm using Typescript. I get this error when I do that.

Expected 1 arguments, but got 0.ts(2554) index.d.ts(2, 41): An argument matching this binding pattern was not provided.

zaguiini commented 4 years ago

That doesn't make sense. That function takes no arguments. Please post some source code.

ataravati commented 4 years ago

@zaguiini

Here's what I have in my index.tsx:

import React from 'react';
import { View } from 'react-native';
import { NavigationStackProp } from 'react-navigation-stack';
import { useFormikContext } from 'formik';
import { FormikWizard } from 'formik-wizard';
import { FormikWizardWrapperProps } from '..//../../types/formik-wizard';
import { Button } from 'react-native-elements';
import { wizardSteps } from './wizard-steps';

export interface FormValues {
  email: string;
  password: string;
  confirmationCode: string;
}

export interface SignUpProps extends FormikWizardWrapperProps<FormValues> {
  navigation: NavigationStackProp;
}

export default class SignUp extends React.Component<SignUpProps> {
  handleSubmit = (values: FormValues) => {
    console.log('Values', values);
  };

  render() {
    return (
      <View style={{ flex: 1 }}>
        <FormikWizard
          steps={wizardSteps}
          onSubmit={(values: FormValues) => this.handleSubmit(values)}
          Form={({ children }: { children: React.ReactNode }) => children}
          render={({
            children,
            wizard,
            isLastStep,
            status,
            goToPreviousStep,
            canGoBack,
            actionLabel,
            steps,
            currentStep,
          }) => {
            const { submitForm, isValid } = useFormikContext<FormValues>();

            return (
              <View style={{ flex: 1 }}>
                {children}
                <Button
                  disabled={!isValid}
                  title={actionLabel || 'Next'}
                  onPress={() => {
                    submitForm().then(() => {
                        console.log("submit...");
                    });
                  }}
                />
              </View>
            );
          }}
        />
      </View>
    );
  }
}

Here are my wizard steps:

import { FormikWizardStepType } from '../../../types/formik-wizard';
import * as Yup from 'yup';
import RegexPatterns from '../../../helpers/regex-patterns';
import AccountInformation from './account-information';
import ConfirmationCode from './confirmation-code';

export const wizardSteps: FormikWizardStepType[] = [
  {
    id: 'accountInformation',
    component: AccountInformation,
    initialValues: {
      email: '',
      password: '',
    },
    validationSchema: Yup.object().shape({
      email: Yup.string()
        .required()
        .email('Please enter a valid email address (e.g. johndoe@mail.com).'),
      password: Yup.string()
        .required()
        .min(8)
        .matches(RegexPatterns.password),
    }),
    onAction: async (sectionValues, formValues) => {
      console.log('Form values', formValues);
    },
  },
  {
    id: 'confirmationCode',
    component: ConfirmationCode,
    initialValues: {
      confirmationCode: '',
    },
    validationSchema: Yup.object().shape({
      confirmationCode: Yup.string().required(),
    }),
  },
];

And, here's the code inside the second step (CodeConfirmation):

import React, { useState, useEffect } from 'react';
import { View } from 'react-native';
import OTPInputView from '@twotalltotems/react-native-otp-input';
import { useFormikContext } from 'formik';
import { FormValues } from './';

const ConfirmationCode = () => {
  // const { values } = useFormikWizardContext<FormValues>(); <--- here I get the error
  const {
    values,
    handleChange,
    handleBlur,
    setFieldTouched,
    setFieldValue,
  } = useFormikContext<FormValues>();
  const [isCodeVerified, setCodeVerification] = useState(false);
  const [isCodeValid, setCodeValidity] = useState(false);

  useEffect(() => {
    console.log('Values', values);
    setFieldTouched('confirmactionCode', true);
  }, []);

  return (
      <View
        style={{
          flex: 1,
          flexGrow: 1,
          alignItems: 'center',
        }}>
        <View>
          <OTPInputView
            style={{ width: '70%', height: 120 }}
            keyboardType="number-pad"
            autoFocusOnLoad={false}
            pinCount={6}
            code={values.confirmationCode}
            onCodeChanged={code => {
              setFieldValue('confirmationCode', code);
              handleChange('confirmationCode');
            }}
            onCodeFilled={code => {
              setCodeVerification(true);
              setCodeValidity(false);
            }}
          />
        </View>
      </View>
  );
};
zaguiini commented 4 years ago

And what this mean?

index.d.ts(2, 41): An argument matching this binding pattern was not provided.

Have you tried to open this declaration file to see where's the conflict?

ataravati commented 4 years ago

Yes, here is what's inside the declaration file:

import { FormikWizardContextValue, FormikWizardProps } from './types';
export declare function FormikWizard<T>({ formikProps, albusProps, onSubmit, steps, Form, render, }: FormikWizardProps<T>): JSX.Element;
export default FormikWizard;
export declare function useFormikWizard<T>(): FormikWizardContextValue<T, any>;

So, do I have to create those properties in index.ts, export them, and then reusing them when using useFormikWizardContext()?

zaguiini commented 4 years ago

My bad! The name of the hook is useFormikWizard. Sorry 😄

ataravati commented 4 years ago

@zaguiini useFormikWizard only has the values for the step. I need to access the entire wizard values. By the way, even inside index.tsx, the form values are always empty. I can only see values in the sectionValues parameter of the onAction of each step.

ataravati commented 4 years ago

Do I need to set the values manually? That's not how it works in the example code.

zaguiini commented 4 years ago

useFormikWizard only has the values for the step

Well, from what I see in your code, you have two values variables declared. Try running like this:

  const { values: wizardValues } = useFormikWizard<FormValues>();

And see the value of the wizardValues variable.

ataravati commented 4 years ago

@zaguiini I was using useFormikContext(). My bad, this time. Sorry! :)

That worked. Perfect!