lost_in_magento
lost_in_magento

Reputation: 693

Using useState hooks to update based on the previous state successively

I'm trying to enable button if both the checkboxes are checked, I have nonworking stackblitz link

I have added the only crux of functionality. Please look into the link for the nonworking demo

    import React, { useState } from 'react';
    import { render } from 'react-dom';

    function App (){
      const [checked, toggleCheckbox] = useState({ checkbox1: false, checkbox2: false, disabled: true });
      const getDisabled = (state) => {
         if (state.checkbox1 || state.checkbox2) {
           return false;
         } else if (!state.checkbox1 && !state.checkbox2) {
           return true;
         } else {
           return true;
         }
       };
       const handleCheckbox = (checkbox) => {
        toggleCheckbox({
            ...checked,
            [checkbox]: !checked[checkbox],
            disabled: getDisabled(checked)
        });
        console.log(checked);
    };
    const checkDisable = checked.disabled ? 'disabled' : ''
    return (
      <div>
        <div>
           <label>
               <input
                    type="checkbox"
                    className="filled-in"
                    onChange={() => handleCheckbox('checkbox1')}
                    checked={checked.checkbox1}
                />
                <span className="black-text">Checkbox1</span>
            </label>
        </div>
        <div>
            <label>
                <input
                    type="checkbox"
                    className="filled-in"
                    onChange={() => handleCheckbox('checkbox2')}
                    checked={checked.checkbox2}
                />
                <span className="black-text">checkbox2</span>
            </label>
        </div>
        <div>
            <a className={checkDisable} href="#!">
                Next Step
            </a>
        </div>
   </div>
  );
}
render(<App />, document.getElementById('root'));

The functionality should be as follows:

  1. The button should be enabled only if both the checkboxes are checked
  2. On unchecking anyone checkboxes it should disable the button

Upvotes: 3

Views: 1502

Answers (1)

dance2die
dance2die

Reputation: 36895

You can simply check the state of both checkbox values.

  const isDisabled = !(checked.checkbox1 && checked.checkbox2)
  const checkDisable = isDisabled ? 'disabled' : ''

No need to change elsewhere.

Forked stackblitz link. https://stackblitz.com/edit/react-jscqwr?file=index.js

demo


Answer to the comment.

Hey, that worked! I could see in the log that the state one step below the updated state for an instance after clicking in the first checkbox { checkbox1: false, checkbox: false, disabled: false } after clicking the second checkbox the log { checkbox1: true, checkbox: false, disabled: false }

The reason you are seeing outdated state is because the state updator toggleCheckbox batches the update, thus you'd need to check for the updated status in an effect, which monitors the updated status.

how to console


Dynamic number of checkboxes.

I've updated the stack to track dynamic number of checkboxes.

New fork~ https://stackblitz.com/edit/react-pl1e2n

Looks like this. enter image description here

function App() {
  const length = 6;
  1️⃣ Generate the initial checkbox states - this prevents un/controlled component error.
  const initialCheckboxes = Array
    .from({ length }, (_, i) => i + 1)
    .reduce((acc, id) => (acc[id] = false, acc), {})

  const [checked, toggleCheckbox] = useState(initialCheckboxes);
  const [isDisabled, setIsDisabled] = useState(false)

  const handleCheckbox = id => {
    toggleCheckbox(previous => ({
      ...previous,
      [id]: !previous[id]
    }));
  };

  2️⃣ Update the disable state when the checkbox is selected.
  useEffect(() => {
     👇 Enable when all checkboxes are not checked - 😅
    setIsDisabled(!Object.values(checked).every(_ => !!_))
  }, [checked])

  3️⃣ Dynamically generate checkboxes
  const checkboxElements = Array.from({ length }, (_, i) => i + 1)
    .map(id => (
      <div key={id}>
        <label>
          <input
            type="checkbox"
            className="filled-in"
            onChange={() => handleCheckbox(id)}
            checked={checked[id]}
          />
          <span className="black-text">Checkbox{id}</span>
        </label>
      </div>
    ))


  return (
    <div>
      {checkboxElements}
      <div>
        <a className={isDisabled ? 'disabled' : ''} href="#!">
          Next Step
                            </a>
      </div>
    </div>
  );
}

Upvotes: 4

Related Questions