0

I have a JSON file that I want to render in react with unknown keys and values (just their types):

{
  "Day 1" :{
    "Chest": {
        "warmUp": ["parallel bar dips"],
        "main": ["Bench Press", "Inclined Bench press", "Decline Bench press"],
        "secondary": ["Dumbbell Flys", "Cable Crossover Flys", "Pec-deck Fly"]
      },
      "Biceps" : {
      "Lola": ["Barbell Curl", "Preacher Curl"],
      "bobo": ["Hammer Curls", "Cable Curl", "Dumbbell Curl"]
    }
  }

I want to Render them so they look like this

  <h2>Day </h2>
    <h4>Chest</h4>
        <h5>warmUp</h5>
         <ul>
          <li>Bench Press</li>
          <li>Inclined Bench press</li>
          <li>Decline Bench press</li>
         </ul>
        <h5>secondary</h5>
          <ul>
            <li>Dumbbell Flys</li>
            <li>Cable Crossover Flys</li>
            <li>Pec-deck Fly</li>
           </ul>

I have a solution https://codesandbox.io/s/cold-sun-vprkl?file=/src/App.js

Object.entries(program).map((arrays) => {
    return arrays.map((renderArr, render_idx) => {
      if (render_idx === 0) {
        return render.push(<h2>{renderArr}</h2>);
      } else {
        for (const [muscleGroup, exerciseGroups] of Object.entries(renderArr)) {
          render.push(<h4>{muscleGroup}</h4>);
          for (const exerciseCategory in exerciseGroups) {
            render.push(<h5>{exerciseCategory}</h5>);
            exerciseGroups[exerciseCategory].map(
              (exercise, exercise_idx) => {
                return render.push(<li>{exercise}</li>);
              }
            );
           ;
          }
        }
      }
    });
  });

but I want to know if there is a better, more elegant way to do this. Also, in my solution I couldn't wrap the ul tags around the list items.

1
  • It would probably help you get an answer if your code sandbox was actually runnable. See minimal reproducible example Commented Jan 19, 2021 at 16:21

2 Answers 2

2

I would split the logic between appropriate small components to improve readability and get closer to the first SOLID principle - single responsibility.

const data = {
  'Day 1': {
    Chest: {
      warmUp: ['parallel bar dips'],
      main: ['Bench Press', 'Inclined Bench press', 'Decline Bench press'],
      secondary: ['Dumbbell Flys', 'Cable Crossover Flys', 'Pec-deck Fly'],
    },
    Biceps: {
      Lola: ['Barbell Curl', 'Preacher Curl'],
      bobo: ['Hammer Curls', 'Cable Curl', 'Dumbbell Curl'],
    },
  },
}

const Program = ({ program }) => {
  const days = Object.entries(program)

  return (
    <section className="Program">
      {days.map(([name, data], index) => (
        <ProgramDay name={name} data={data} key={index} />
      ))}
    </section>
  )
}

const ProgramDay = ({ name, data }) => {
  const parts = Object.entries(data)

  return (
    <div className="ProgramDay">
      <h2>{name}</h2>

      {parts.map(([name, data], index) => (
        <ProgramPart name={name} data={data} key={index} />
      ))}
    </div>
  )
}

const ProgramPart = ({ name, data }) => {
  const types = Object.entries(data)

  return (
    <div className="ProgramPart">
      <h2>{name}</h2>

      {types.map(([name, exercises], index) => (
        <ProgramExercises name={name} exercises={exercises} key={index} />
      ))}
    </div>
  )
}

const ProgramExercises = ({ name, exercises }) => {
  return (
    <div className="ProgramExercises">
      <h5>{name}</h5>

      <ul>
        {exercises.map((name, index) => (
          <li key={index}>{name}</li>
        ))}
      </ul>
    </div>
  )
}

ReactDOM.render(<Program program={data} />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="app"></div>

Sign up to request clarification or add additional context in comments.

Comments

1

Your approach can work, but it's very un-React-like. A far more React-like approach would leverage JSX much more heavily (and use logic inside JSX), and it would also use multiple components and return instead of building arrays.

Imagine instead ...

const Main = () =>
  Object.entries(program).map(arrays => 
    arrays.map((renderArr, index) =>
     {index === 0 
       ? <h2>{muscleGroups}</h2>
       : <MuscleGroups muscleGroups={muscleGroups} />
     }
  );

const MuscleGroups = ({ muscleGroups }) =>
  Object.entries(muscleGroups).map(([muscleGroup, exerciseGroups]) =>
    <MuscleGroup muscleGroup={muscleGroup} exerciseGroups={exerciseGroups}/>
  );

const MuscleGroup = ({ muscleGroup, exerciseGroups }) =>
  <>
    <h4>{muscleGroup}</h4>
    {Object.entries(exerciseGroups).map(([exerciseCategory, exerciseGroup]) => 
      <ExcerciseGroup category={exerciseCategory} exerciseGroup={exerciseGroup}/>
    )}
  </>

const ExerciseGroup = ({ exerciseCategory, exerciseGroup }) => 
  <>
    <h5>{exerciseCategory}</h5>
    <ul>
      {exerciseGroup.map(exercise => <li>{exercise}</li>)}
    </ul>
  </>;

P.S. Apologies if there are any typos in there (it's hard to refactor lots of code outside an editor). But even if there are some, hopefully you can see the overall idea I'm trying to convey.

You don't have to use as many components as I did (eg. you could combine MuscleGroups and MuscleGroup if you wanted). Like anything in code, it's ultimately subjective as to what's the "best" way.

But in a larger sense, you should really have the same guiding principle as with regular Javascript code. Just as you wouldn't want one giant function that does everything in JS (you'd want several smaller, focused functions), you likewise want to break your React logic into several focused components.

Comments

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.