Home > Back-end >  Accessing value of child component within Formik form
Accessing value of child component within Formik form

Time:12-22

I have a form that uses Formik and contains two fields - email and siteID

The email field uses a TextInput whereas siteID comes from my own component called DropDown that uses react-native-dropdown-picker

When my form is submitted I can see the value of email within handleSubmit() but siteID remains as 0 and not the selected value.

How can I access the selected value from my DropDown child component when submitting the parent form?

My form

return (
  <SafeAreaView style={{ flex: 1 }}>
    <View style={styles.loginContainer}>
      <Formik
        validationSchema={loginValidationSchema}
        initialValues={{
          siteID: 0,
          email: "",
        }}
        onSubmit={(values) => handleSubmit(values)}
      >
        {({
          handleChange,
          handleBlur,
          handleSubmit,
          values,
          errors,
          isValid,
        }) => (
          <>
            <Text style={styles.label}>Email</Text>
            <TextInput
              name="email"
              style={styles.textInput}
              onChangeText={handleChange("email")}
              onBlur={handleBlur("email")}
              value={values.email}
              keyboardType="email-address"
            />

            <DropDown name="siteID" />
          </>
        )}
      </Formik>
    </View>
  </SafeAreaView>
);

DropDown component

import * as React from 'react';
import {StyleSheet} from 'react-native';
import DropDownPicker from 'react-native-dropdown-picker';
import {usePostRequest} from '../../client';
import {useState, useEffect} from 'react';

const DropDown = () => {
  const [pickeropen, pickersetOpen] = useState(false);
  const [pickervalue, pickersetValue] = useState(null);
  const [pickeritems, pickersetItems] = useState([]);

  const {status: siteStatus, data: siteData} = usePostRequest('/api/sites', {});

  useEffect(() => {
    console.log('siteData', siteData);
    if (siteData.count) {
      const sitesList = [];
      siteData.results.map((item, key) =>
        sitesList.push({label: item.siteName, value: item.siteID})
      );
      pickersetItems(sitesList);
    }
  }, [siteData]);

  return (
    <DropDownPicker
      style={styles.textInput}
      placeholder="Please chose a site"
      open={pickeropen}
      value={pickervalue}
      items={pickeritems}
      setOpen={pickersetOpen}
      setValue={pickersetValue}
      setItems={pickersetItems}
    />
  );
};

CodePudding user response:

You can use setFieldValue provided by Formik to set a field manually.

A good example of usage is provided in this example

You can pass setFieldValue to your DropDown Component:

return (
  <SafeAreaView style={{ flex: 1 }}>
    <View style={styles.loginContainer}>
      <Formik
        validationSchema={loginValidationSchema}
        initialValues={{
          siteID: 0,
          email: "",
        }}
        onSubmit={(values) => handleSubmit(values)}
      >
        {({
          handleChange,
          handleBlur,
          handleSubmit,
          values,
          errors,
          isValid,
          setFieldValue,
        }) => (
          <>
            <Text style={styles.label}>Email</Text>
            <TextInput
              name="email"
              style={styles.textInput}
              onChangeText={handleChange("email")}
              onBlur={handleBlur("email")}
              value={values.email}
              keyboardType="email-address"
            />

            <DropDown name="siteID" setSiteID={setFieldValue} />
          </>
        )}
      </Formik>
    </View>
  </SafeAreaView>
);

And trigger it from inside of the DropDown Component:

const DropDown = ({ setSiteID }) => {
  const [pickeropen, pickersetOpen] = useState(false);
  const [pickervalue, pickersetValue] = useState(null);
  const [pickeritems, pickersetItems] = useState([]);

  const {status: siteStatus, data: siteData} = usePostRequest('/api/sites', {});

  useEffect(() => {
    console.log('siteData', siteData);
    if (siteData.count) {
      const sitesList = [];
      siteData.results.map((item, key) =>
        sitesList.push({label: item.siteName, value: item.siteID})
      );
      pickersetItems(sitesList);
    }
  }, [siteData]);
  
  // You can use useEffect or create a custom handleValueChange 
  // and pass it to DropDownPicker
  useEffect(() =>  {
    setSiteID('siteID', pickervalue);
  }, [pickersetValue])

  return (
    <DropDownPicker
      style={styles.textInput}
      placeholder="Please chose a site"
      open={pickeropen}
      value={pickervalue}
      items={pickeritems}
      setOpen={pickersetOpen}
      setValue={pickersetValue}
      setItems={pickersetItems}
    />
  );
};

CodePudding user response:

Based on the docs, to use your own component, you should wrap your component in a Field as follows:

const DropDownField = <Field name="siteId" component={DropDown} />

When using a Field formik will inject props (name, value, onChange, onBlur) into your component allowing you to control the form state.

So you can then redeclare your component with those props as follows:

import * as React from 'react';
import {StyleSheet} from 'react-native';
import DropDownPicker from 'react-native-dropdown-picker';
import {usePostRequest} from '../../client';
import {useState, useEffect} from 'react';

const DropDown = ({ field: { name, value, onChange }) => {
  const [pickeropen, pickersetOpen] = useState(false);
  const [pickeritems, pickersetItems] = useState([]);

  const {status: siteStatus, data: siteData} = usePostRequest('/api/sites', {});

  useEffect(() => {
    console.log('siteData', siteData);
    if (siteData.count) {
      const sitesList = [];
      siteData.results.map((item, key) =>
        sitesList.push({label: item.siteName, value: item.siteID})
      );
      pickersetItems(sitesList);
    }
  }, [siteData]);

  return (
    <DropDownPicker
      style={styles.textInput}
      placeholder="Please chose a site"
      open={pickeropen}
      value={value}
      items={pickeritems}
      setOpen={pickersetOpen}
      setValue={onChange}
      setItems={pickersetItems}
      name={name}
    />
  );
};

Essentially you wire your component up to the onChange and value props. Since you assigned a name of siteId to the DropDownField component (which you should now use in your form), any time you call onChange site Id will update.

Link to docs

CodePudding user response:

try this,

get setFieldValue from formik props.

add onChangeDropdown to <Dropdown /> like,

<DropDown name="siteID" onChangeDropdown={(value)=>{ setFieldValue("siteID",value) }}/>

get the props inside Dropdown component,

const DropDown = (props) => {

add onChangeValue prop to your DropDownPicker

onChangeValue={props.onChangeDropdown}

  • Related