react-redux icon indicating copy to clipboard operation
react-redux copied to clipboard

State updates not working in React custom hooks

Open dnsnx opened this issue 2 years ago • 2 comments

What version of React, ReactDOM/React Native, Redux, and React Redux are you using?

  • React: 18.2.0
  • ReactDOM/React Native: 18.2.0
  • Redux: 4.2.0
  • React Redux: 7.2.8

What is the current behavior?

I'm not 100% if this is really a bug or a wrong usage / expectation. I'll try to describe the situation.

This is my custom hook, where I want to use the current value of productSetupWizard .

    import { isNullOrUndefined } from '../../../../../../utils/utils';
    import { useSelector } from 'react-redux';
    
    export const useGoodsSetupWizardController = () => {
        const productSetupWizard = useSelector((state) => state.productSetupWizard.value);
    
        function isNextButtonDisabled() {
            let returnValue = false;
            switch(productSetupWizard?.step) {
                case 2:
                    if(isNullOrUndefined(productSetupWizard?.product?.productName) || productSetupWizard?.product?.productName.trim().length === 0) {
                        returnValue = true;
                    }
            }
            return returnValue;
        }
    
        return {
            isNextButtonDisabled,
        };
    }

As you can see I'm using useSelector to get the current value of productSetupWizard. In my other components and hooks all is working fine using this way. The slice is this one here:

    import { createSlice } from '@reduxjs/toolkit';
    
    const initialState = {
      value: { 
        step: 1, 
        product: null 
      },
    };
    
    export const productSetupWizardSlice = createSlice({
      name: 'productSetupWizard',
      initialState,
      reducers: {
        setProductSetupWizard: (state, action) => {
          state.value = action.payload
        },
        increaseStep: (state, action) => {
          state.value.step++;
        },
        decreaseStep: (state, action) => {
          state.value.step--;
        }
      },
    });
    
    export const { setProductSetupWizard, increaseStep, decreaseStep } = productSetupWizardSlice.actions;
    
    export default productSetupWizardSlice.reducer;

I don't know if you need to see initializing of the store as well. If so, please let me know. I will add this then, too. So the value of productSetupWizard in method isNextButtonDisabled() is always the initial value:

 value: { 
        step: 1, 
        product: null 
      },

Here is the complete way of importing the different custom hooks I use in my project. I have a hook called ProductSetupWizard. This is my UI. This ProductSetupWizard is importing the function getController from the custom hook called useProductTypeSetupWizard:

    import { useProductTypeSetupWizard } from '../../../../factories/productSetup.factory';
    
    function ProductSetupWizard(props) {
        const [productSetupController, setProductSetupController] = useState(null);
        ...
        const { getController } = useProductTypeSetupWizard();
        ...
        
        useEffect(() => {
                setProductSetupController(getController(productTypeId));
        },[productTypeId]);
       
        return (
            <>
            ...
            </>
        );
    }

The useProductTypeSetupWizard hook is loading and returning the useGoodsSetupWizardController depending on the given productTypeId:

    import { isNullOrUndefined } from "../utils/utils";
    import { useGoodsSetupWizardController } from '../components/managementInterface/shop/tabs/productSetupWizard/setups/goodsSetupWizard.controller';
    
    export const useProductTypeSetupWizard = () => {
        const {
            getComponentGoodsController,
            isNextButtonDisabledGoodsController,
            needsNextStepToBeSkippedGoodsController
        } = useGoodsSetupWizardController();
    
        function getController(productTypeId) {
            if(isNaN(parseInt(productTypeId))) {
                return null;
            }
            switch(parseInt(productTypeId)) {
                case 1:
                    return {
                        getComponent: getComponentGoodsController,
                        isNextButtonDisabled: isNextButtonDisabledGoodsController,
                        needsNextStepToBeSkipped: needsNextStepToBeSkippedGoodsController
                    };
                case 3:
                    //apply like useGoodsSetupWizardController
            }
        }
    
        return { 
            getController
        };
    }

This is the situation. I'm not sure, if it is maybe an issue of how I use the state value with different custom hooks.

What is the expected behavior?

The expected behaviour should be, that I'm getting the current (updated) value of productSetupWizard. The value is 100% updated in the store, but I did not get this value from the store, but only the initial one. Here is one example of an updated state value:

 value: { 
        step: 3, 
        product: {
              productName: 'Some name',
              productNo: 123
        }
 },

Which browser and OS are affected by this issue?

Chrome (104.0.5112.102) / Windows 11 (Build 22000.856)

Did this work in previous versions of React Redux?

  • [ ] Yes

dnsnx avatar Aug 26 '22 10:08 dnsnx

I'm not sure I understand the actual problem statement. Can you clarify where you're expecting to see the updated state, and when?

It would really help if you could put together a CodeSandbox or a Github repo that shows this happening, or create and share a Replay ( https://replay.io/record-bugs ) of this bug that I can look at.

markerikson avatar Aug 26 '22 15:08 markerikson

Ok, I will provide a codepen soon.

dnsnx avatar Aug 27 '22 12:08 dnsnx

Closing due to lack of response.

markerikson avatar Nov 04 '22 02:11 markerikson