Search code examples
reactjsreduxredux-toolkit

getting the changed value in state


I have a project that sends values to a server when the state changes via websocket and i'm using redux-toolkit and I have a state like this

const initialState = {
  exposure: false,
  whiteBalance: false,
  shutterSpeed: 1,
  iso: 50,
  temperature : 5500,
  tint : 0,
  brightness: { checked: false, value: 0 },
  hue: { checked: false, value: 0 },
  saturation: { checked: false, value: 0 },
  vibrance: { checked: false, value: 0 },
  contrast: { checked: false, value: 0 },
  gamma: { checked: false, value: 0 },
  sharpness: { checked: false, value: 0 },
};

const imageAdjustmentsSlice = createSlice({
  name: "imageAdjustments",
  initialState,
  reducers: {
    changeIso: (state, action: PayloadAction<number>): void => {
      state.iso = action.payload;
    },
    changeTemperature: (state,action: PayloadAction<number>): void => {
      state.temperature = action.payload;
    },
    changeTint: (state,action: PayloadAction<number>): void => {
      state.tint = action.payload;
    },
    // and other reducers
  }
});

const store = configureStore({  
  reducer: {
    imgAdjustments:imageAdjustmentsSlice.reducer,
  },
});
const WSController: React.FC = () => {
      const videoStore = useAppSelector((state) => state.imgAdjustments);
      const { sendMessage } = useWebSocket("ws://localhost:3000");
    
      React.useEffect(() => {
        // i want to send the changed value only not the whole state.
        sendMessage(JSON.stringify(videoStore));
      }, [videoStore ]);

      return <></>
}

When videoStore changes i want to send the changed values to server. I want to know which value changes when the state changes. E.g. i changed iso value in somewhere using dispatch. I just want to get the changed value which is iso. Like this :

const changedValue = getChangedValue(state) // returns { iso : 200 }

Is there function/hook or another way to do this ? Or should i reconfigure redux?


Solution

  • There's no "built-in" React or React-Redux/Redux-Toolkit hook for this behavior/logic so you'd need to implement it yourself. I'd suggest implementing a custom React hook that can compute the difference between a current and previous object value.

    Example:

    import { useEffect, useRef } from "react";
    
    // Typical "usePrevious" value hook recipe
    const usePrevious = (value) => {
      const ref = useRef();
    
      useEffect(() => {
        ref.current = value;
      });
    
      return ref.current;
    };
    
    const useObjectDiff = (current) => {
      const previous = usePrevious(current);
    
      const diff = {};
    
      if (current && previous) {
        Object.entries(current).forEach(([key, value]) => {
          if (previous[key] !== value) {
            diff[key] = value;
          }
        });
      }
    
      return Object.keys(diff).length ? diff : null;
    };
    

    Demo:

    Edit getting-the-changed-value-in-state

    Usage:

    const WSController = () => {
      const videoStore = useAppSelector((state) => state.imgAdjustments);
      const { sendMessage } = useWebSocket("ws://localhost:3000");
    
      const videoStoreDiff = useObjectDiff(videoStore);
        
      React.useEffect(() => {
        if (videoStoreDiff) {
          sendMessage(JSON.stringify(videoStore));
        }
      }, [videoStoreDiff]);
    
      return (
        ...
      );
    }