-2

I have a JS object myFieldsObj as below;

{
    field1: {value: 1},
    field2: {value: 2},
    field3: {value: 3}
}

In my JSX, I want to iterate over this and render some output.

<MyCustomDialog>
{
<>
Object.keys(myFieldsObj).map((key, index) => {
    <div key={index}>
        <Field field={myFieldsObj[key]} />
    </div>
})
</>
}
</MyCustomDialog>

However the above is giving an error saying

Uncaught ReferenceError: index is not defined

Not sure what the issue is with the syntax.

copenndthagen
  • 49,230
  • 102
  • 290
  • 442
  • 3
    There are a least a couple of things going on there. The primary thing is that you're missing `{` between `<>` and `Object.keys`, so `Object.keys` and what follows is just text being rendered. (You'd also need a `}` before the `>`.) There's no need for that outer wrapper and fragment, though, just `{Object.keys(/*...*/).map(/*....*/)}`. There's also [this issue](https://stackoverflow.com/questions/45754957/why-doesnt-my-arrow-function-return-a-value) with your arrow function. – T.J. Crowder May 14 '23 at 16:36
  • 4
    I'd strongly recommend reading https://react.dev/learn/writing-markup-with-jsx and https://react.dev/learn/javascript-in-jsx-with-curly-braces, because most of that is wrong. – jonrsharpe May 14 '23 at 16:39

1 Answers1

1

To try and tie TJ's and Jon's comments up in a working example with some additional commentary.

  1. Since you're trying create Field components within another bespoke component (MyCustomDialog) you'll need to have MyCustomDialog render those children. You can do that by ensuring that children is accessed from its props and returned inside a (semantic) element (here: section). Note: wrapping the child elements in a presentation element here means you can do away with the React fragment (<>) in your other component.

  2. Make sure that you return each Field component within the map. You're using curly braces so an explicit return is required. There's also no real need for that separate div wrapper. Just apply the key to the field component instead.

(More after the first example...)

const { useState } = React;

// MyCustomDialog needs to be able to
// render its child elements. Since we wrap them
// in a section element we can do without the
// React fragment in the Example component
function MyCustomDialog({ children }) {
  return <section>{children}</section>;
}

// Basic field component
function Field({ field }) {
  return <div>{field.value}</div>;
}

// Passing in the data to the component, and
// iterating over it. On each iteration a new
// field component it created with a new field object
function Example({ myFieldsObj }) {
  return (
    <MyCustomDialog>
      {Object.keys(myFieldsObj).map((key, index) => {
        return <Field key={index} field={myFieldsObj[key]} />
      })}
    </MyCustomDialog>  
  );
}

const myFieldsObj = {
  field1: { value: 1 },
  field2: { value: 2 },
  field3: { value: 3 }
};

const node = document.getElementById('root');
const root = ReactDOM.createRoot(node);
root.render(<Example myFieldsObj={myFieldsObj} />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.min.js"></script>
<div id="root"></div>
  1. You may find it easier using an array for your data instead of an object. Note here the objects in the array all have an id property the value of which you can use for the key in the map iteration instead of map's own index which you should generally avoid.

    In this example, in the iteration, I'm adding the each complete object as data in the props so I can access both the name and the value in the Field component.

const { useState } = React;

// MyCustomDialog needs to be able to
// render its child elements. Since we wrap them
// in a section element we can do without the
// React fragment in the Example component
function MyCustomDialog({ children }) {
  return <section>{children}</section>;
}

// Basic field component
function Field({ data }) {
  return (
    <div>
      <h4>{data.name}</h4>
      <p>{data.value}</p>
    </div>
  );
}

// Passing in the data to the component, and
// iterating over it. On each iteration a new
// field component it created with a new field object
function Example({ myFields }) {
  return (
    <MyCustomDialog>
      {myFields.map(obj => {
        return (
          <Field
            key={obj.id}
            data={obj}
          />
        );
      })}
    </MyCustomDialog>  
  );
}

const myFields = [
  { id: 1, name: 'field1', value: 1 },
  { id: 2, name: 'field2', value: 2 },
  { id: 3, name: 'field3', value: 3 }
];

const node = document.getElementById('root');
const root = ReactDOM.createRoot(node);
root.render(<Example myFields={myFields} />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.min.js"></script>
<div id="root"></div>
Andy
  • 61,948
  • 13
  • 68
  • 95