eBay / eBay/nice-modal-react

Using nested NiceModalProvider for new window

Open
#141 4 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
TypeScript
Stars
2.3k
Forks
127
PR merge metrics
No merged PRs in 30d

Description

Hello, thanks for this good library. I've always used it well.
I'm opening this issue because I have a suggestion.

I found that the dispatch is declared as global variable. In a situation that you have a single provider, this isn't a big deal. But this is problem if you are using more than one provider.

When NiceModal.provider is called, the global dispatch is updated with givenDispatch or new dispatch function that returned from useReducer hook.

const InnerContextProvider: React.FC = ({ children }) => {
  const arr = useReducer(reducer, initialState);
  const modals = arr[0];
  dispatch = arr[1]; // update with new dispatch
  return (
    <NiceModalContext.Provider value={modals}>
      {children}
      <NiceModalPlaceholder />
    </NiceModalContext.Provider>
  );
};

export const Provider: React.FC<Record<string, unknown>> = ({
  children,
  dispatch: givenDispatch,
  modals: givenModals,
}: {
  children: ReactNode;
  dispatch?: React.Dispatch<NiceModalAction>;
  modals?: NiceModalStore;
}) => {
  if (!givenDispatch || !givenModals) {
    return <InnerContextProvider>{children}</InnerContextProvider>;
  }
  dispatch = givenDispatch; // update with givenDispatch
  return (
    <NiceModalContext.Provider value={givenModals}>
      {children}
      <NiceModalPlaceholder />
    </NiceModalContext.Provider>
  );
};

If you use multiple providers, the global dispatch will be updated with dispatch function intended by the last called provider. And every modals will only be rendered under this last provider because all methods in library (like NiceModal.show) use global dispatch.

function SomeApp() {
  return (
    <NiceModal.Provider> // update global dispatch
      <SomeArea />
      <NiceModal.Provider> // update global dispatch again
        // the modal dispatched in 'SomeArea' will be rendered here 
      </NiceModal.Provider />
    <NiceModal.Provider />
  )
}

I know that this is not common case.
In our team, we open new window and render components with react portal. In this case we should use multiple providers to render modals in new window.

function NewWindow({ children }) {
  const [container] = useState(() => {
    const newWindow = window.open()
    const container = newWindow.document.createElement('div')
    newWindow.document.body.append(container)
    return container
  })

  return ReactDOM.createPortal(children, container)
}

function SomeApp() {
 return (
    <NiceModal.Provider>
      ...
      <NewWindow>
        <NiceModal.Provider>
          ...
        </NiceModal.Provider>
      </NewWindow>
    </NiceModal.Provider>
   )
}

So I suggest following.

  • provide dispatch function with context
    const InnerContextProvider: React.FC = ({ children }) => {
      const arr = useReducer(reducer, initialState);
      const modals = arr[0];
      dispatch = arr[1];
    
      return (
        <NiceModalContext.Provider value={modals}>
          <NiceModalDispatchContext.Provider value={dispatch}
            {children}
          </NiceModalDispatchContext.Provider>
          <NiceModalPlaceholder />
        </NiceModalContext.Provider>
      );
    };
    
  • use the dispatch from context in showCallback that is returned by useModal hook
    export function useModal(modal?: any, args?: any): any {
      ...
      const dispatch = useContext(NiceModalDispatchContext)
      ...
      const showCallback = useCallback((...) => {
        ...
        dispatch(showModal(...))
      }, [...]
    }
    

Thanks for reading.
Please consider and respond.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the Provider, InnerContextProvider, NiceModalContext, and useModal entry points described in the issue, then trace how NiceModal.show and showCallback obtain dispatch. Verify nested providers and the NewWindow portal case before changing behavior; done means each provider dispatches and renders its own modals without the global dispatch being overwritten.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.