[SOLVED] Limit number of checkboxes selected and save value

Issue

I am building a food delivery application, and I would like to know how I can limit the number of checkboxes selected. An example is when entering the subsidiary, it displays a list of products. If I select a pizza, there is an extras section that limits the number of extras you can select, if you want to select more than two and your limit is two it should not allow you

all this with react hooks, I attach a fragment of my component

const ExtrasSelector = ({options = [{}], onPress = () => {}, limit = 0}) => {
  const [showOptions, setShowOptions] = useState(true);
  const [selectedAmount, setSelectedAmount] = useState(0);
  const EXTRA = ' extra';
  const EXTRAS = ' extras';

  const updatedList = options.map(data => ({
    id: data.id,
    name: data.name,
    price: data.price,
    selected: false,
  }));

  const [itemsList, setItemsList] = useState(updatedList);

  const toggleOptions = () => setShowOptions(!showOptions);

  useEffect(() => {

  }, [selectedAmount]);

  // onPress for each check-box
  const onPressHandler = index => {
    setItemsList(state => {
      state[index].selected = !state[index].selected;
      onPress(state[index], getSelectedExtras(state));

      // Increments or decreases the amount of selected extras
      if (state[index].selected) {
        setSelectedAmount(prevState => prevState + 1);
      } else {
        setSelectedAmount(prevState => prevState - 1);
      }
  
      return state;
    });
  };

  const getSelectedExtras = extrasArr => {
    const selectedExsArr = [];
    extrasArr.map(item => {
      if (item.selected) {
        selectedExsArr.push(item);
      }
    });

    return selectedExsArr;
  };

  return (
    <View>
      <View style={styles.container}>
        <TouchableOpacity style={styles.row} onPress={toggleOptions}>
          <Text style={styles.boldTitleSection}>
            Extras {'\n'}
            <Text style={titleSection}>
              Selecciona hasta {limit}
              {limit > 1 ? EXTRAS : EXTRA}
            </Text>
          </Text>
          <View style={styles.contentAngle}>
            <View style={styles.contentWrapperAngle}>
              <Icon
                style={styles.angle}
                name={showOptions ? 'angle-up' : 'angle-down'}
              />
            </View>
          </View>
        </TouchableOpacity>

        {showOptions ? (
          itemsList.map((item, index) => (
            <View key={index}>
              <CheckBox
                label={item.name}
                price={item.price}
                selected={item.selected}
                otherAction={item.otherAction}
                onPress={() => {
                  onPressHandler(index, item);
                }}
              />
              <View style={styles.breakRule} />
            </View>
          ))
        ) : (
          <View style={styles.breakRule} />
        )}
      </View>
    </View>
  );
};

Solution

This is a simple react implementation of "checkboxes with limit" behaviour with useReducer. This way the business logic (here the limitation but can be any) is implemented outside of the component in a pure js function while the component itself is just a simple reusable checkbox group.

const { useReducer } = React; // --> for inline use
// import React, { useReducer } from 'react';  // --> for real project


const reducer = (state, action) => {
  if (state.checkedIds.includes(action.id)) {
    return {
      ...state,
      checkedIds: state.checkedIds.filter(id => id !== action.id)
    }
  }
  
  if (state.checkedIds.length >= 3) {
    console.log('Max 3 extras allowed.')
    return state;
  }
  
  return {
    ...state,
    checkedIds: [
      ...state.checkedIds,
      action.id
    ]
  }
}


const CheckBoxGroup = ({ data }) => {
  const initialState = { checkedIds: [] }
  const [state, dispatch] = useReducer(reducer, initialState)
 
  return (
    <table border="1">
      {data.map(({ id, label }) => (
        <tr key={id}>
          <td>
            <input
              onClick={() => dispatch({ id })}
              checked={state.checkedIds.includes(id)}
              type="checkbox"
            />
          </td>
          <td>
            {label}
          </td>
        </tr>
      ))}      
    </table>
  )
};


 const data = [
    { id: "1", label: "Mashroom" },
    { id: "2", label: "Ham" },
    { id: "3", label: "Egg" },
    { id: "4", label: "Ananas" },
    { id: "5", label: "Parmesan" },    
 ]

ReactDOM.render(<CheckBoxGroup data={data} />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Answered By – gazdagergo

Answer Checked By – Marie Seifert (BugsFixing Admin)

Leave a Reply

Your email address will not be published.