Is it possible to manage different switch cases in reducer function of useReducer hook on a single button?

813 views Asked by At

I have a question "Make a “room” with a light that has 4 levels – off, low, medium, high – and change the level each time you press a button. Create a second button to turn the lights off." I'm assuming that I need to manage three levels i.e. low, medium, high by pressing a single button like we toggle states and have to use another button for turning the light off. But my knowledge is limited to managing each switch case of a reducer on a separate button.

1

There are 1 answers

1
Lafi On BEST ANSWER

So here is a solution with and without useReducer, but first lets set the states config in a new helper file stateHelper.js:

export const states = {
  off: {
    value: "off",
    next: "low",
  },
  low: {
    value: "low",
    next: "medium",
  },
  medium: {
    value: "medium",
    next: "hight",
  },
  hight: {
    value: "hight",
    next: "low",
  },
};

export const opacities = {
  off: 1,
  low: 0.3,
  medium: 0.7,
  hight: 1,
};

Solution without useReducer:

import React, { useCallback, useState } from 'react';
import { states, opacities } from './statesHelper';

function AppWithoutUseReducer() {
    const [currentState, setState] = useState(states.off);

    const handleToggle = useCallback(
        () => setState(states[currentState.next]),
        [currentState.next]
    );

    const handleSetOff = useCallback(
        () => setState(states[states.off.value]),
        []
    );

    return (
        <div
            style={{
                margin: '50px',
                border: '1px solid',
            }}
        >
            <center style={{ opacity: opacities[currentState.value] }}>
                <h1>{currentState.value}</h1>
            </center>
            <div style={{ display: 'flex', justifyContent: 'center' }}>
                <button onClick={handleToggle}>toggle</button>
                <button onClick={handleSetOff}>off</button>
            </div>
        </div>
    );
}

Solution with useReducer:

import React, { useCallback, useReducer } from 'react';

import { states, opacities } from './statesHelper';

// const appReducer = (state, action) => {
//   switch (action.type) {
//     case states.off.value:
//       return states.off;
//     case states.low.value:
//       return states.low;
//     case states.medium.value:
//       return states.medium;
//     case states.hight.value:
//       return states.hight;
//     default:
//       return state;
//   }
// };

// appReducer is refactored to appReducerGeneric
const appReducerGeneric = (state, action) => {
    if (states[action.type]) {
        return states[action.type];
    }
    return state;
}; // it's like we don't need a useReducer ??

function AppUsingUseReducer() {
    const [currentState, dispatch] = useReducer(appReducerGeneric, states.off);

    const handleToggle = useCallback(
        () => dispatch({ type: currentState.next }),
        [currentState.next]
    );

    const handleSetOff = useCallback(
        () => dispatch({ type: states.off.value }),
        []
    );

    return (
        <div
            style={{
                margin: '50px',
                border: '1px solid',
            }}
        >
            <center style={{ opacity: opacities[currentState.value] }}>
                <h1>{currentState.value}</h1>
            </center>
            <div style={{ display: 'flex', justifyContent: 'center' }}>
                <button onClick={handleToggle}>toggle</button>
                <button onClick={handleSetOff}>off</button>
            </div>
        </div>
    );
}

export default AppUsingUseReducer;

This is a codesandbox demo.