skip to Main Content

I have a grid with multiple cells that the user can select.
Depending on the cells the user selects on the grid, I show different actions at the bottom of the page. The way I’m implementing this right now is as such:

const allActionsToShow = {
    action1: false,
    action2: false,
    action3: false,
    action4: false
};

selectedCells.map(cell => {
    if (cell.id === ‘cell1’) { 
        allActionsToShow.action1 = true;
        allActionsToShow.action2 = true;
        hideRemainingActions([‘action3’, ‘action4’]);
    }
    if (cell.id === ‘cell3’) {
        allActionsToShow.action3 = true;
        hideRemainingActions([‘action1’, ‘action2’, ‘action4’])
    }
    if (cell.id === ‘cell4’) {
        allActionsToShow.action4 = true;
        hideRemainingActions([`action1’, `action2`, action3’])
    }
});

const hideRemainingActions = (actionsToHide: string[]) => {
    actionsToHide.map(actionId => {
        allActionsToShow[actionId] = false;
    });
};

Is there a more efficient way of doing this? If there are 50 actions for example, then I have to manually set those 50 to true depending on the cell selected (if conditions above) and then pass in the actions I don’t want to show to the hideRemainingActions function. This would make it difficult to scale.

2

Answers


  1. I would probably do it a bit differently.. since you tagged reactjs I wrote up a rough component that you might be able to make use of.

    First, you’re using map to iterate through the selectedCells list, but map returns an array that you’re not using, so it’s not exactly the right tool for the job here. Instead I’d just loop through the array using a forEach.

    I’d define an object that specificies the permissions for each cell, and I’d do it outside the component so it didn’t get redefined on every render, or need to be passed to useEffect and such.

    I’d have a piece of state for the permitted actions, and I’d set them accordingly..

    Finally, somewhere in the return, I’d only print the actions that were still permitted.

    EDIT: I thought of another way to do it that I like better because it doesn’t require iterating through the entire list of cells on every click.. This might be more efficient. I’ll keep the original idea at the bottom.

    Here’s the new idea

    // with this method, we only have to define the actions that aren't allowed by each cell
    let permissions = {
        cell1: {
            action3: false,
            action4: false,
        },
        cell3: {
            action1: false,
            action2: false,
            action4: false,
        },
        cell4: {
            action1: false,
            action2: false,
            action3: false,
        }
    };
    
    const Whatever = props => {
        
        // all actions are permissable by default, or you can set these to false if you want
        const [permittedActions,setPermittedActions] = useState({
            action1: { permitted: true, because: [] },
            action2: { permitted: true, because: [] },
            action3: { permitted: true, because: [] },
            action4: { permitted: true, because: [] },
        });
    
        let handleSelect = cell => {
            if ( permissions[cell.id] ) {
                // there are some rules defined for this cell, let's apply them
                setPermittedActions(previousState=>{
                    let newState = {...previousState};
                    for (const [key, value] of Object.entries(permissions[cell.id])) {
                        if ( !value ) {
                            // whatever action this is, it's not permitted by this cell
                            // we'll keep track of all the cells that don't permit this action
                            newState[key].permitted = false;
                            newState[key].because.push(cell.id);
                        }
                    }
                    return newState;
                });
            }
        }; // handleSelect
    
        let handleDeselect = cell => {
            if ( permissions[cell.id] ) {
                // again, rules have been set up for this cell, let's review them
                setPermittedActions(previousState=>{
                    let newState = {...previousState};
                    for (const [key, value] of Object.entries(permissions[cell.id])) {
                        if ( !value ) {
                            // this cell did not permit the action in question. Let's remove it
                            // from the list
                            newState[key].because = newState[key].because.filter(cellID=>cellID !== cell.id);
    
                            // are there any cells left that didn't permit this action? If not, we can
                            // set it to be permitted
                            if ( !newState[key].because.length ) {
                                newState[key].permitted = true;
                            }
                        }
                    }
                    return newState;
                });
            }
        }; // handleSelect
    
        return (
            <div>
                {
                    // whatever you need for your grid etc etc
                    // make sure to add the handlers for when each cell is selected and de-selected
                }
                { permittedActions.action1.permitted && ( <div>action 1 stuff here</div> ) }
                { permittedActions.action2.permitted && ( <div>action 2 stuff here</div> ) }
                { permittedActions.action3.permitted && ( <div>action 3 stuff here</div> ) }
                { permittedActions.action4.permitted && ( <div>action 4 stuff here</div> ) }
            </div>
        );
    }; // Whatever
    

    Here’s the old idea

    let permissions = {
        cell1: {
            action1: true,
            action2: true,
            action3: false,
            action4: false,
        },
        cell3: {
            action1: false,
            action2: false,
            action3: true,
            action4: false,
        },
        cell4: {
            action1: false,
            action2: false,
            action3: false,
            action4: true,
        }
    };
    
    const Whatever = props => {
        
        // all actions are permissable by default, or you can set these to false if you want
        const [permittedActions,setPermittedActions] = useState({
            action1: true,
            action2: true,
            action3: true,
            action4: true
        });
        
        useEffect(()=>{
            /* we're going to loop through the list of selected cells and check if
            * a rule has been set for that cell. If so, we'll appy those rules.
            * We'll start out by assuming all actions are permitted, and we'll
            * turn some of them to false as necessary
            */
            let permitted = {
                action1: true,
                action2: true,
                action3: true,
                action4: true
            };
            selectedCells.forEach(cell=>{
                if ( permissions[cell.id] ) {
                    // ok there's a list of rules for this cell. let's check them
                    for (const [key, value] of Object.entries(permissions[cell.id])) {
                        // is this action still permitted? Is the current rule telling us not to permit it?
                        // if so then we must set this action to false.
                        if ( permitted[key] && !value ) {
                            permitted[key] = false;
                        }
                    }
                }
            });
            // ok let's update our state with the new object
            setPermittedActions( permitted );
        },[
            selectedCells
        ]);
    
        return (
            <div>
                {
                    // whatever you need for your grid etc etc
                }
                { permittedActions.action1 && ( <div>action 1 stuff here</div> ) }
                { permittedActions.action2 && ( <div>action 2 stuff here</div> ) }
                { permittedActions.action3 && ( <div>action 3 stuff here</div> ) }
                { permittedActions.action4 && ( <div>action 4 stuff here</div> ) }
            </div>
        );
    }; // Whatever
    
    Login or Signup to reply.
  2. such function may help if I understood well what you want to do this function receives an array keysToSetToTrue where each element is a string indicating the name of the key we want to set it to true

    const actionsToHide = (keysToSetToTrue) => {
      let updateddActions = { ...allActionsToShow };
      Object.keys(updateddActions).forEach((key) => updateddActions[key] = false);
      keysToSetToTrue.forEach((key) => {
        updateddActions[key] = true;
      });
      let arrayToReturn = [];
      Object.keys(updateddActions).forEach((key) => {
        if (updateddActions[key] === false) arrayToReturn.push(key);
      });
      return arrayToReturn;
    };
    

    first we copy the object then we set all its keys values to false since it will be overridden each time hideRemainingActions runs, after that we map through keysToSetToTrue array and set each corresponding key value of updateddActions to true, then, we just need to return an array where each element is the name of the key we want to hide so we can pass directly the result of this function to hideRemainingActions

    selectedCells.map(cell => {
     if (cell.id === ‘cell1’) { 
        hideRemainingActions(actionsToHide(["action1","action2"]));
     } 
     //...
    })
    

    Try it

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search