-2

I'm just getting started on learning Reactjs in a laravel project, and I'm stuck on what should be a simple problem. I've passed some data that I want to present in a table organised by category and month. The data is an array of objects like this.

Budget

  • id
  • category
  • month
  • budget

Not all combinations will have data (eg only certain months are represented for a particular category) so I need to use external keys to ensure I have the right number of table cells. I have a separate list of categories that I can map, but I'm not sure how to handle looping through the months since JSX doesn't seem to like my for loop.

export default function Budget({ auth, errors, categories, budget }) {
    return (
        <AuthenticatedLayout
            user={auth.user}
            header={<h2 className="font-semibold text-xl text-gray-800 leading-tight">Budget</h2>}
        >
            <Head title="Budget" />

            <div className="py-12">
                <div className="mx-auto sm:px-6 lg:px-8">
                    <div className="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                        <div className="p-6 text-gray-900 flex flex-wrap">
                            <div>Category</div>
                            <div>Jan</div>
                            <div>Feb</div>
                            <div>Mar</div>
                            <div>Apr</div>
                            <div>May</div>
                            <div>Jun</div>
                            <div>Jul</div>
                            <div>Aug</div>
                            <div>Sep</div>
                            <div>Oct</div>
                            <div>Nov</div>
                            <div>Dec</div>
                            <div>Total</div>
                            <div>Previous Year</div>
                        </div>
                        {categories.map((category, index) => (
                            <div>
                                <div>{category.name}</div>
                                {for(let m = 1; m < 13; m++) {
                                    <div>{budget.find(entry => (entry.category === category.id && entry.month == m))}</div>
                                }}
                            </div>
                        ))}
                    </div>
                </div>
            </div>
        </AuthenticatedLayout>
    );
}

This gives me an 'expression expected' error at the 'for'. Other solutions I've seen suggest creating an array (1-12) to map, which seems like a poor solution. Do I need to create a component for the row so that I can use the 'for' before the return in each? That seems like a waste since I'll never use it anywhere else in this or any other project.

What's best practice in cases like this?

maganthro
  • 361
  • 1
  • 3
  • 10
  • 3
    Why don't you `.map`, as you're already doing over the categories? – jonrsharpe May 30 '23 at 21:27
  • see [this answer](https://stackoverflow.com/questions/22876978/loop-inside-react-jsx?rq=2) – sid_mac May 30 '23 at 21:31
  • @jonrsharpe, because that would require creating an array to count 1-12, which as I said, seems like a poor solution. Is that the best way? – maganthro May 30 '23 at 23:07
  • @sid_mac, that's the best thread I found and where I got the suggestion of creating an array. It also had many other suggestions, which require creating a component. The solutions with a for loop inside the return don't compile for me. Is there a particular solution in this page which you think works? – maganthro May 30 '23 at 23:10
  • using a component would also require me to send the whole 'budget' prop through to the component on each iteration (so I can perform the search outside the return), which seems extremely wasteful. This wouldn't be a problem if I could structure my array, but everything I've read says that's problematic if the data is dynamic as this will be. So far React seems a poor system all around, but I guess I just don't understand its value yet. – maganthro May 31 '23 at 00:48
  • 1
    You have to create the array one way or another. No matter if it's for loop or slice – Konrad May 31 '23 at 06:38
  • Who sets these questions as 'typos' and not helpful to others? This is the second one labeled that way that wasn't caused by a typo. This is a genuine problem understanding react by a newbie and I'm sure Konrad's answer will help other newbies struggling with similar questions. Please remove the ridiculous categorisation. – maganthro May 31 '23 at 16:05

2 Answers2

2

You can either make a function that creates that array:

const getBudgets = (category) => {
  const budgets = [];
  for (let m = 1; m < 13; m++) {
    budgets.push(<div>{category[m].budget}</div>);
  }
  return budgets;
};

...

<>
  {categories.map((category, index) => (
    <div>
      <div>{category.name}</div>
      {getBudgets(category)}
    </div>
  ))}
</>;

Or use slice and map

<>
  {categories.map((category, index) => (
    <div>
      <div>{category.name}</div>
      {category.slice(0, 13).map((cat) => (
        <div>{cat.budget}</div>
      ))}
    </div>
  ))}
</>;
Konrad
  • 21,590
  • 4
  • 28
  • 64
  • This works. I'm still not sure why my 'for' was causing an 'expression expected' error yesterday, but knowing that I have to create a array answers my question. I'm still not a fan of react, but I'll keep going. – maganthro May 31 '23 at 16:08
  • for loop is a statement, you can use only expressions in-between jsx code – Konrad May 31 '23 at 17:42
0

The problem with your for loop is that you are using category as if it was an object and an array of objects. In this line <div>{category.name}</div> you are accessing a property which suggests an object but in this line <div>{category[m].budget}</div> you are accessing an element which suggests an array. I think you may have meant to reference an element of budget? <div>{category.budget[m]}</div>

I would also question the range used in your for loop. JavaScript arrays are 0 based by default so unless you have overridden the default behavior it should probably be {for(let m = 0; m < 12; m++) {.

If you fix the array issues would the suggestion made in the comments, to simply use another loop for the months, work?

Leigh.D
  • 463
  • 5
  • 13
  • You're right that I mucked up the category in my code (mixing two different attempts). I was getting an error on the 'for' though, so I never made it that far. – maganthro May 31 '23 at 16:02