Why are my state values breaking on dispatch?

Viewed 34

I am working on a react app where I have a userSettings screen for the user to update their settings on clicking a save button. I have two sliding switches that are saved and a dispatch function is ran to post the data.

Each switch has their own toggle function, and all the functions run at the same time.

My problem is that when I pass the userSettings object to the child component and run both functions, it runs with the wrong values which results in the data not saving properly.

Here is my code:

Parent component that has the toggle functions, handles the state, and set the userSettings object:

class SideMenu extends React.PureComponent {
  constructor(props) {
    super(props);

    const userToggleSettings = {
      cascadingPanels: this.props.userSettings.usesCascadingPanels,
      includeAttachments: this.props.userSettings.alwaysIncludeAttachments,
      analyticsOptIn: false,
    };

    this.state = {
      userToggleSettings,
    };
  }

  toggleIncludeAttachments = () => {
    this.setState((prevState) => ({
      userToggleSettings: {
        ...prevState.userToggleSettings,
        includeAttachments: !prevState.userToggleSettings.includeAttachments,
      },
    }));
  };

  toggleCascadingPanels = () => {
    this.setState((prevState) => ({
      userToggleSettings: {
        ...prevState.userToggleSettings,
        cascadingPanels: !prevState.userToggleSettings.cascadingPanels,
      },
    }));
  };

  includeAttachmentsClickHandler = () => {
    this.toggleIncludeAttachments();
  };

  cascadingPanelsClickHandler = () => {
    this.toggleCascadingPanels();
  };

  render() {
    const darkThemeClass = this.props.isDarkTheme ? "dark-theme" : "";
    const v2Class = this.state.machineCardV2Enabled ? "v2" : "";
    const phAdjustmentStyle = this.getPersistentHeaderAdjustmentStyle();
    const closeButton =
      (this.state.machineListV2Enabled &&
        this.props.view === sideMenuViews.USER_SETTINGS) ||
      (!this.props.wrapper && this.props.view === sideMenuViews.SETTINGS);

    return (
      <div className="sideMenuFooter">
        <SideMenuFooterContainer
          userToggleSettings={this.state.userToggleSettings} //HERE IS USER_SETTINGS PASSED
        />
      </div>
    );
  }
}

The child component that dispatches the data

SideMenuFooterContainer:

export function mapStateToProps(state) {
  return {
    translations: state.translations,
    userSettings: state.appCustomizations.userSettings,
  };
}

export function mapDispatchToProps(dispatch) {
  return {
    toggleCascadingPanels: (hasCascadingPanels) =>
      dispatch(userSettingsDux.toggleCascadingPanels(hasCascadingPanels)),
    toggleIncludeAttachments: (hasIncludeAttachments) =>
      dispatch(userSettingsDux.toggleIncludeAttachments(hasIncludeAttachments)),
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(SideMenuFooter);

SideMenuFooterView (where it calls the dispatch):


const saveUserSettings = (props) => {
  console.log("userSettings ==>\n");
  console.log(props.userToggleSettings);
  props.toggleIncludeAttachments(props.userToggleSettings.includeAttachments);
  props.toggleCascadingPanels(props.userToggleSettings.cascadingPanels);
};

const cancelButtonClickHandler = (props) => {
  if (props.viewTitle === props.translations.USER_SETTINGS) {
    return () => props.closeSideMenu();
  }

  return () => props.viewBackButtonCallback();
};

const doneSaveButtonsClickHandler = (props) => {
  return () => {
    saveUserSettings(props);
    props.closeSideMenu();
  };
};

const SideMenuFooter = (props) => {
  return (
    <div className="side-menu-footer">
      <div className="side-menu-footer-container">
        <button
          className="btn btn-secondary"
          onClick={cancelButtonClickHandler(props)}
        >
          {props.translations.CANCEL}
        </button>
        <button
          className="btn btn-primary"
          onClick={doneSaveButtonsClickHandler(props)}
        >
          {props.translations.SAVE}
        </button>
      </div>
    </div>
  );
};

export default SideMenuFooter;

Dispatch functions:

export function toggleIncludeAttachments(hasIncludeAttachments) {
  return async (dispatch, getState) => {
    const { translations, appCustomizations } = getState();

    const updatedUserSettings = {
      ...appCustomizations.userSettings,
      alwaysIncludeAttachments: hasIncludeAttachments,
    };

    try {
      await saveAppCustomizationByName(
        CUSTOMIZATIONS.USER_SETTINGS,
        updatedUserSettings
      );

      dispatch(setSettings(updatedUserSettings));
    } catch (err) {
      dispatch(
        bannerDux.alertBanne({
          description: "FAILED TO UPDATE USER DATA",
        })
      );
    }
  };
}

export function toggleCascadingPanels(hasCascadingPanels) {
  return async (dispatch, getState) => {
    const { translations, appCustomizations } = getState();

    const updatedUserSettings = {
      ...appCustomizations.userSettings,
      usesCascadingPanels: hasCascadingPanels,
    };

    try {
      await saveAppCustomizationByName(
        CUSTOMIZATIONS.USER_SETTINGS,
        updatedUserSettings
      );

      dispatch(setSettings(updatedUserSettings));
    } catch (err) {
      dispatch(
        bannerDux.alertBanner({
          description: "FAILED TO UPDATE USER DATA",
        })
      );
    }
  };
}

Here is a demo:

When I set them both to false and console log the values, it looks like it is getting the correct values, but in the network call, it is getting different values on different calls

enter image description here

console.log output:

enter image description here

First network call to save data header values:

enter image description here

Second network call to save data header values:

enter image description here

NOTE: The dispatch functions work correctly, they where there before all the edits. I am changing the way it saves the data automatically to the save button using the same functions defined before.

Did I miss a step while approaching this, or did I mishandle the state somehow?

0 Answers
Related