6

**EDIT: Please check the updated sandbox, it incorporates some of the changes suggested in the below answer. **

I'm using Formik and its <FieldArray ../> component to handle some nested data to render inputs. I can't quite figure out what exact string to pass to the name prop in <FieldArray name={ // here}.

In my component I have a user selection that filters out some data and returns this array.

"chosenDevice": [
  {
    "deviceName": "eth0",
    "macAddress": "01:40:27:0F:2E:CB",
    "ipv4DHCP": false,
    "ipv4Addresses": [
      "182.148.1.100/24"
    ],
    "ipv4Gateway": "",
    "ipv6DHCP": false,
    "ipv6Addresses": [
      "232.232.2/100/10"
    ],
    "ipv6Gateway": ""
  }
]

I want to access the ipv4Addresses array. I'm either passing the wrong string to name or not using index in .map properly. Currently I have the following:

    <FieldArray
  name={`chosenDevice.ipv4Addresses[${index}]`}
>
  {({ remove, push }) => (
    <>
      <div>
        {values.chosenDevice.length > 0 &&
          values.chosenDevice.map(
            (ipv4Addresses, index) => (
              <div
                style={{
                  display: "flex"
                }}
                key={index}
              >
                <label
                  htmlFor={`chosenDevice[${index}].ipv4Addresses`}
                  className="custom-label"
                  style={{ flex: 1 }}
                >
                  ipv4Address(es)
                  <Field
                    className="custom-input"
                    name={`chosenDevice[${index}].ipv4Addresses`}
                    placeholder="< ipv4Address >"
                    type="text"
                  />
                </label>
                <ErrorMessage
                  name={`chosenDevice.${index}.ipv4Addresses`}
                  component="div"
                  className="field-error"
                />

I have a codesanbox that may provide some more context.

After looking into this further, basically I need to provide the string value of deviceName[0].ipv4Addresses[0], so I may need to rethink the .map and what it's actually returning.

3 Answers 3

4

With a .map inside of another .map, you might be getting index values confused. Try naming index something more specific to each .map (such as deviceIndex and addressIndex). I've used addressIndex for the inner map operation in my answer below.


It seems like your <FieldArray> here should only concern itself with the ipv4Addresses array on the current chosenDevice object.

Try this:

<FieldArray
  name="chosenDevice.ipv4Addresses"
>
  {({ remove, push }) => (
    <>
      <div>
        {chosenDevice.ipv4Addresses > 0 &&
          chosenDevice.ipv4Addresses.map(
            (ipv4Address, addressIndex) => (
              <div
                style={{
                  display: "flex"
                }}
                key={addressIndex}
              >
                <label
                  htmlFor={`chosenDevice.ipv4Addresses.${addressIndex}`}
                  className="custom-label"
                  style={{ flex: 1 }}
                >
                  ipv4Address(es)
                  <Field
                    className="custom-input"
                    name={`chosenDevice.ipv4Addresses.${addressIndex}`}
                    placeholder="< ipv4Address >"
                    type="text"
                  />
                </label>
                <ErrorMessage
                  name={`chosenDevice.ipv4Addresses.${addressIndex}`}
                  component="div"
                  className="field-error"
                />

But if I'm wrong about that, and the <FieldArray> tag does need to reference which chosenDevice it's a child of, this should do it:

<FieldArray
  name={`chosenDevice.${index}.ipv4Addresses`}
>
  {({ remove, push }) => (
    <>
      <div>
        {chosenDevice[index].ipv4Addresses > 0 &&
          chosenDevice[index].ipv4Addresses.map(
            (ipv4Address, addressIndex) => (
              <div
                style={{
                  display: "flex"
                }}
                key={addressIndex}
              >
                <label
                  htmlFor={`chosenDevice.${index}.ipv4Addresses.${addressIndex}`}
                  className="custom-label"
                  style={{ flex: 1 }}
                >
                  ipv4Address(es)
                  <Field
                    className="custom-input"
                    name={`chosenDevice.${index}.ipv4Addresses.${addressIndex}`}
                    placeholder="< ipv4Address >"
                    type="text"
                  />
                </label>
                <ErrorMessage
                  name={`chosenDevice.${index}.ipv4Addresses.${addressIndex}`}
                  component="div"
                  className="field-error"
                />
Sign up to request clarification or add additional context in comments.

5 Comments

@dj2 I've updated my answer just now with a few syntax corrections. Sorry about the initial errors.
I've tried both solutions in my codesandbox example and the field doesn't seem to render with the changes. Did you fork the sandbox by any chance?
The second solution, it's just pushing an empty string to the array without actually rendering another input box.
The map is working correctly because I added > 1 ipv4Address and it's display them in separate fields. So the remove(ipv4Index) may need to be changed
Any more thoughts on this?
1
<FieldArray name={`chosenDevice.ipv4Addresses[${index}]`}>

The index here if you are trying to use from the map below, then this is wrong you'll not get index outside the map.

Use this map above the FieldArray, like this:

values.chosenDevice.map((ipv4Addresses, index) => (
 <FieldArray
  name={`ipv4Addresses[${index}]`}
>
  {({ remove, push }) => (
    <>
      <div>
              <div
                style={{
                  display: "flex"
                }}
                key={index}
              >
                <label
                  htmlFor={`chosenDevice[${index}].ipv4Addresses`}
                  className="custom-label"
                  style={{ flex: 1 }}
                >
                  ipv4Address(es)
                  <Field
                    className="custom-input"
                    name={`chosenDevice[${index}].ipv4Addresses`}
                    placeholder="< ipv4Address >"
                    type="text"
                  />
                </label>
                <ErrorMessage
                  name={`chosenDevice.${index}.ipv4Addresses`}
                  component="div"
                  className="field-error"
                />)

4 Comments

Trying these changes, I am not able to add additional fields for ipv4Addresses. Additionally, when there are 2+ addresses being read in, both the strings are in one input instead of two.
If you are saying so then I suggest you use state for accessing index outside map <FieldArray name={ipv4Addresses[${index}]}
I have updated the sandbox with your suggested changes codesandbox.io/s/network-config-wrlcz.
Tried using state?
1
  const [configIdx, setConfigIdx] = useState(0);

 <FieldArray
           name={`chosenDevice.ipv4Addresses[${configIdx}]`}
                                    >
                                      {({ remove, push }) => (
                                        <>
                                          <div>
                                            {values.chosenDevice.length > 0 &&
                                              values.chosenDevice.map(
                                                (ipv4Addresses, index) => {
                                                  setConfigIdx(index);
                                                  return (
                                                    <div
                                                      style={{
                                                        display: "flex"
                                                      }}
                                                      key={index}
                                                    >
                                                      <label

See how I updating state, this might help to achieve your result.

4 Comments

Are you able to update the sandbox? I've tried several approaches with no success.
Yes but locally
Can you fork it and paste the link with your updates?
That I can't promise because my system is off now but you can copy above code and paste it appropriately

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.