Contextual Save Bar - React component

Highlighted
Shopify Partner
9 0 9

Hi everyone, I created a Contextual Save Bar component for react. The Polaris docs recommend to use the app bridge bar and app-bridge-react doesn't contain a component for that. So I made one! Cheers.

 

import { useContext, useEffect, useState } from 'react'
import { Context as ShopifyContext } from '@shopify/app-bridge-react'
import { ContextualSaveBar } from '@shopify/app-bridge/actions'

function useContextualSaveBar(save, discard) {
  const [shouldSave, setShouldSave] = useState(false);
  const [shouldDiscard, setShouldDiscard] = useState(false);

  useEffect(() => {
    if (shouldSave) {
      save[0]();
      setShouldSave(false);
    }
  }, [shouldSave, ...save]);

  useEffect(() => {
    if (shouldDiscard) {
      discard[0]();
      setShouldDiscard(false);
    }
  }, [shouldDiscard, ...discard]);

  return [() => setShouldSave(true), () => setShouldDiscard(true)];
}

const SaveBar = ({ isShown, save, discard }) => {

  const [onSave, onDiscard] = useContextualSaveBar(save, discard);
  const app = useContext(ShopifyContext);

  const create = () => {
    const options = {
      message: 'Unsaved changes',
      saveAction: {
        disabled: false,
        loading: false,
      },
      discardAction: {
        disabled: false,
        loading: false,
        discardConfirmationModal: true,
      },
    };
    return ContextualSaveBar.create(app, options);
  };

  const [saveBar] = useState(create());

  useEffect(() => {
    const saveUnsub = saveBar.subscribe(
      ContextualSaveBar.Action.SAVE,
      onSave
    );

    const discardUnsub = saveBar.subscribe(
      ContextualSaveBar.Action.DISCARD,
      onDiscard
    );

    return () => {
      saveUnsub();
      discardUnsub();
    };
  }, []);

  if (isShown) {
    saveBar.dispatch(ContextualSaveBar.Action.SHOW);
  } else {
    saveBar.dispatch(ContextualSaveBar.Action.HIDE);
  }

  return null;
}

export default SaveBar

Use:

  const [showSaveBar, setShowSaveBar] = useState(false);

  const onSave = () => {
doWork(stateA, stateB, stateC); }; const onDiscard = () => {
const {stateA, stateB, stateC} = initialState; // via useState(getInitialState())
setStateA(stateA);
setStateB(stateB);
setStateC(stateC); }; <SaveBar isShown={showSaveBar} save={[onSave, stateA, stateB, stateC]} discard={[onDiscard, initialState]} />

 

2 Likes
Highlighted
Shopify Partner
9 0 9

I made an update to this code. It adds the <Loading /> bar and disabling save.

 

import { useContext, useEffect, useState } from 'react'
import { Context as ShopifyContext, Loading } from '@shopify/app-bridge-react'
import { ContextualSaveBar } from '@shopify/app-bridge/actions'

function useContextualSaveBar(save, discard) {
  const [shouldSave, setShouldSave] = useState(false);
  const [shouldDiscard, setShouldDiscard] = useState(false);

  useEffect(() => {
    if (shouldSave) {
      save[0]();
      setShouldSave(false);
    }
  }, [shouldSave, ...save]);

  useEffect(() => {
    if (shouldDiscard) {
      discard[0]();
      setShouldDiscard(false);
    }
  }, [shouldDiscard, ...discard]);

  return [() => setShouldSave(true), () => setShouldDiscard(true)];
}

const SaveBar = ({ showBar, showSaveLoading, disableSave, save, discard }) => {

  const options = {
    saveAction: {
      disabled: disableSave,
      loading: showSaveLoading,
    },
    discardAction: {
      disabled: false,
      loading: false,
      discardConfirmationModal: true,
    },
  };

  const [saveBar] = useState(ContextualSaveBar.create(useContext(ShopifyContext), options));
  saveBar.set(options, true);

  const [onSave, onDiscard] = useContextualSaveBar(save, discard);

  useEffect(() => {
    const saveUnsub = saveBar.subscribe(
      ContextualSaveBar.Action.SAVE,
      onSave
    );

    const discardUnsub = saveBar.subscribe(
      ContextualSaveBar.Action.DISCARD,
      onDiscard
    );

    return () => {
      saveUnsub();
      discardUnsub();
    };
  }, []);

  showBar
    ? saveBar.dispatch(ContextualSaveBar.Action.SHOW)
    : saveBar.dispatch(ContextualSaveBar.Action.HIDE);

  return showSaveLoading
    ? <Loading />
    : null;
}

export default SaveBar
2 Likes