skip to Main Content

I’m trying to wrap my head around the Context concept in React, I want to use it to create a reusable confirm modal with the minimum possible boilerplate (for the modal consumer), in my mind, and due to my misunderstanding of how context works (like a useState), this is what I imagine:

From the main App, you import the component and the launcher function:

import {ConfirmDialog, ShowConfirmModal} from './components/ConfirmDialog';

And inside components/ConfirmDialog.js you have:

import React, {createContext, useContext} from "react";
import {ModalDialog, Text} from "@forge/react";

const context = createContext({ isOpen: false })

export const ConfirmDialog = () => {

    const _context = useContext(context)

    return _context.isOpen && <ModalDialog header={_context.title} onClose={_context.onClose} closeButtonText='Confirm'>

        <Text>{_context.message}</Text>

    </ModalDialog>

}

export const ShowConfirmModal = (title, message, onClose) => {

    const [_context, _setContext] = useContext(context)

    _setContext({
        message, title, onClose, isOpen: true
    })

}

This will allow the consumer to add the component in the root of the document and to show it simply by calling the ShowConfirmModal function. BUT, this does not work, since I cannot use: const [_context, _setContext] = useContext(context) And now I don’t understand what will be the best approach to solve this or even if mine is correct (with some modifications).

I’m new to React and I what I want more than a solution to the issue, is an orientation, is my reasoning correct? Or am I not "thinking in React"? (I’m a MVC dev)

2

Answers


  1. First of all you need a provider component:

    const DialogContext= createContext(undefined);
    
    export const ConfirmDialogProvider = ({ children }) => {
      // declare your state here
      const [isOpen, setIsOpen] = useState(false);
      const [message, setMessage] = useState(undefined);
    
      return (
        <DialogContext.Provider value={{ isOpen, setIsOpen, message, setMessage }}>
          <>
            {children}
            <ModalDialog header={title} onClose={onClose} closeButtonText="Confirm">
              <Text>{message}</Text>
            </ModalDialog>
          </>
        </DialogContext.Provider>
      );
    };
    

    You then have to use your provider in an ancestor of all components that want to open a modal
    e.g.

    function App() {
      return (
        <ConfirmDialogProvider>
          <Other>
            <Components />
            <Here />
          </Other>
        </ConfirmDialogProvider>
      )
    }
    

    Now you can use your context:

    const SomeComponent = () => {
      const { setIsOpen, setMessage } = useContext(DialogContext);
      return (
        <button
          onClick={() => {
            setIsOpen(true);
            setMessage("Title");
          }}
        >
          Open modal
        </button>
      );
    };
    

    Notes:

    • You can declare your modal in a separate component and use useContext(DialogContext) there, but it must be added to the tree anyway
    • You can group your state, so you don’t have to use multiple functions to change it
    • You can create a helper hook like const useDialogContext = useContext(DialogContext) so you don’t have to write that much
    Login or Signup to reply.
  2. Your approach is correct, but there are few suggestions about how React context works
    you can follow these steps to make some corrections

    1. Create a context hook like this:
      the default value should include the properties that you are accessing in your components.

    `

    const ConfirmDialogContext = createContext({
          isOpen: false,
          title: "",
          message: "",
          onClose: () => {},
        });`
    
    1. Provide Component

    The provider sets the values for the context

    export const ConfirmDialogProvider = ({ children }) => {
      const [context, setContext] = useState({
        isOpen: false,
        title: "",
        message: "",
        onClose: () => {},
      });
    
      return (
        <ConfirmDialogContext.Provider value={{ ...context, setContext }}>
          {children}
        </ConfirmDialogContext.Provider>
      );
    };
    

    3.Use the context hook

    ConfirmDialog component should use the useContext hook to get the context.

    export const ConfirmDialog = () => {
      const { isOpen, title, message, onClose } = useContext(ConfirmDialogContext);
    
      return isOpen && (
        <ModalDialog header={title} onClose={onClose} closeButtonText='Confirm'>
          <Text>{message}</Text>
        </ModalDialog>
      );
    };
    

    Show Confirmation Modal

    export const ShowConfirmModal = (title, message, onClose) => {
      const { setContext } = useContext(ConfirmDialogContext);
    
      setContext({
        isOpen: true,
        title,
        message,
        onClose,
      });
    };
    

    Finally use that ConfrimDialogProvider in APP funtion

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search