Conditional rendering in React based on the onClick property of the IconButton component in Material UI

import "./styles.css";
import React, { useState } from "react";
import { IconButton } from "@material-ui/core";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";

export default function App() {
  const [expand, setExpand] = useState({
    arrowA: false,
    arrowB: false,
    arrowC: false,
    arrowD: false
  });

  const handleChange = (e) => {
    setExpand({
      ...expand,
      [e.currentTarget.name]: !expand.arrowA,
      [e.currentTarget.name]: !expand.arrowB,
      [e.currentTarget.name]: !expand.arrowC,
      [e.currentTarget.name]: !expand.arrowD
    });
  };

  return (
    <div className="App">
      <h1>React conditional rendering</h1>
      <h2>
        The component below should render if the user clicks on the arrow, and
        be removed if the user clicks the arrow again.
      </h2>

      <h5>
        This arrow should show the first part of the text.{" "}
        <IconButton
          variant="contained"
          size="small"
          color="primary"
          aria-label="expand"
          name="arrowA"
          onClick={handleChange}
        >
          {<ExpandMoreIcon />}
        </IconButton>
      </h5>

      <h5>
        This arrow show the second part of the text if clicked, and be possible
        to remove.
        <IconButton
          variant="contained"
          size="small"
          color="primary"
          aria-label="expand"
          name="arrowB"
          onClick={handleChange}
        >
          {<ExpandMoreIcon />}
        </IconButton>
      </h5>

      <h5>
        This arrow should show the third part of the text below.{" "}
        <IconButton
          variant="contained"
          size="small"
          color="primary"
          aria-label="expand"
          name="arrowC"
          onClick={handleChange}
        >
          {<ExpandMoreIcon />}
        </IconButton>
      </h5>

      <h5>
        This was really the last part.{" "}
        <IconButton
          variant="contained"
          size="small"
          color="primary"
          aria-label="expand"
          name="arrowD"
          onClick={handleChange}
        >
          {<ExpandMoreIcon />}
        </IconButton>
      </h5>
      {expand.arrowA ? (
        <h2>
          This is the first start of the story, let's see if we can add the
          rest.
        </h2>
      ) : null}

      {expand.arrowB ? (
        <h2>This is the middle part, it starts to break soon.</h2>
      ) : null}
      {expand.arrowC ? (
        <h2>
          We are nearing the end. But as you see, this state management is not
          optimal.
        </h2>
      ) : null}

      {expand.arrowD ? (
        <h2>
          This is was all I wanted to show. But why is removing this so hard?
          How to make this better?
        </h2>
      ) : null}
    </div>
  );
}

first of all, thanks for being here! I am a beginner in React and have a question regarding how to make my state management more optimal and better working.

I am trying to render multiple components in one React hook, to avoid having to create many different hooks. But my solution isn’t working properly and not very optimal. I lack knowledge how to make the state more dynamic, but I feel I am close. Here is the example in: Code Sandbox

If you open the Code Sandbox, in the second attempt file, I am trying to set [e.currentTarget.value]: [e.currentTarget.status] to try to get the status from the IconButton, which I there store as status={!expand.checkedA} . But that isn’t how React state management works apparently 😅.

Could someone enlighten me on what’s best practice here? If you have a recommendation for an alternative way to conditionally render multiple components, I’d also be curious.

Cheers.

Answer

I think you’re doing fine I would just change a few things to make it work:

First make the handleChange do this instead of the current code:

const handleChange = (e) => {
  setExpand({
   ...expand,
   [e.currentTarget.name]: !expand[e.currentTarget.name]
  });
};

In this way you will only change the one you’re clicking and the rest will stay the same.

Secondly you don’t need the ternary operator in your jsx, you can use the && operator:

  {expand.arrowA && (
    <h2>
      This is the first start of the story, let's see if we can add the
      rest.
    </h2>
  )}

Check the forked sandbox if you have questions: https://codesandbox.io/s/cool-fog-h7vct?file=/src/App.js:2073-2226

Leave a Reply

Your email address will not be published. Required fields are marked *