Home > Software design >  How to style each element individually based on their boolean property?
How to style each element individually based on their boolean property?

Time:01-12

I have DATA array in which i store objects that contain sentences and answers. Each answer has its isCorrect prop.

How do I check answer correctnes on button click?

After button click, correct answers should make <select> tag border change to green and incorrect or undefined to red.

Edit nameless-star-s8xl0f

Parent component:

export default function App() {
  const [isCorrect, setIsCorrect] = useState();
  const [isChecked, setIsChecked] = useState(false);

  const handleValueChange = (isCorrect) => {
    setIsCorrect(isCorrect);
  };

  return (
    <Wrapper>
      {DATA.map((sentence, index) => (
        <Sentence isCorrect={isChecked ? isCorrect : null}>
          <span>
            {index   1}. {sentence.sentenceFirstPart}
          </span>
          <select>
            {sentence.answerOptions.map((option) => (
              <option
                onChange={() => handleValueChange(option.answerText)}
                value={option.answerText}
              >
                {option.answerText}
              </option>
            ))}
          </select>
          <span>{sentence.sentenceSecondPart}</span>
        </Sentence>
      ))}
      <button onClick={() => setIsChecked(true)}>Check</button>
    </Wrapper>
  );
}

Styled components:

const Wrapper = styled.div`
  display: flex;
  flex-direction: column;
  gap: 1rem;

  button {
    padding: 0rem 1rem;
    width: 6rem;
  }
`;

export const Sentence = styled.div`
  display: flex;
  align-items: center;
  span {
    font-size: 1.2rem;
  }

  select {
    margin: 0 0.5rem;
    border: 1px solid;
    padding: 0.2rem;
    border-radius: 4px;
    font-weight: 500;
    border-color: ${({ isCorrect }) =>
      isCorrect ? "green" : !isCorrect && isCorrect !== undefined ? "red" : ""};
  }
`;

Test data:

const DATA = [
  {
    sentenceFirstPart: "It takes a lot of",
    sentenceSecondPart: "to learn how to waterski properly",
    answerOptions: [
      { answerText: "", isCorrect: false },
      { answerText: "attempt", isCorrect: false },
      { answerText: "effort", isCorrect: true },
      { answerText: "trial", isCorrect: false },
      { answerText: "try", isCorrect: false }
    ]
  },
  {
    sentenceFirstPart: "It was Thomas Edison who ",
    sentenceSecondPart: "electricity",
    answerOptions: [
      { answerText: "", isCorrect: false },
      { answerText: "detected", isCorrect: false },
      { answerText: "invented", isCorrect: true },
      { answerText: "found", isCorrect: false },
      { answerText: "discovered", isCorrect: false }
    ]
  }
];

CodePudding user response:

It seems that isCorrect would need to record result of each questions and handleValueChange should probably be called on change of select to update the values in isCorrect.

Forked example with modification: codesandbox

Here isCorrect is updated as an object to save result of each question:

const [isCorrect, setIsCorrect] = useState({});
const [isChecked, setIsChecked] = useState(false);

const handleValueChange = (value, index) => {
  setIsCorrect((prev) => ({
    ...prev,
    [index]: value === "true" ? true : false,
  }));
};

handleValueChange modified to pass value of option and index of the question, so isCorrect can be updated accordingly:

<Wrapper>
  {DATA.map((sentence, index) => (
    <Sentence isChecked={isChecked} isCorrect={isCorrect[index]} key={index}>
      <span>
        {index   1}. {sentence.sentenceFirstPart}
      </span>
      <select onChange={(e) => handleValueChange(e.target.value, index)}>
        {sentence.answerOptions.map((option, index) => (
          <option value={option.isCorrect} key={index}>
            {option.answerText}
          </option>
        ))}
      </select>
      <span>{sentence.sentenceSecondPart}</span>
    </Sentence>
  ))}
  <button onClick={() => setIsChecked(true)}>Check</button>
</Wrapper>

In styled Sentence the select could take both isChecked and isCorrect (for this question) as conditions to set the border-color.

export const Sentence = styled.div`
  display: flex;
  align-items: center;
  span {
    font-size: 1.2rem;
  }

  select {
    margin: 0 0.5rem;
    border: 2px solid;
    padding: 0.2rem;
    border-radius: 4px;
    font-weight: 500;
    border-color: ${({ isChecked, isCorrect }) =>
      !isChecked ? "currentColor" : isCorrect ? "green" : "red"};
  }
`
  • Related