testshallpass / react-native-dropdownalert

An alert to notify users about an error or something else
MIT License
1.85k stars 253 forks source link

Use with Hooks #225

Open blackwolf08 opened 4 years ago

blackwolf08 commented 4 years ago

Short Description

Improves Documentation

Steps to Reproduce / Code Snippets / Usage

import React, { useRef } from "react";
import DropdownAlert from "react-native-dropdownalert";

const DropDownContext = React.createContext();

export const DropDownComponentProvider = ({ children }) => {
  let ref = useRef();
  return (
    <DropDownContext.Provider
      value={{
        ref,
      }}
    >
      {children}
      <DropdownAlert ref={ref} />
    </DropDownContext.Provider>
  );
};

export const useDropDown = () => React.useContext(DropDownContext);

In App.js

import React from "react";
import { AppearanceProvider } from "react-native-appearance";
import {
  AuthProvider,
  DropDownComponentProvider,
  ThemeProvider,
  UserProvider,
} from "./contexts";
import { firebaseInit } from "./firebase";
import NavigationConatiner from "./navigation";

// init firebase app
firebaseInit();

export default function App() {

  return (
    <DropDownComponentProvider>
      <UserProvider>
        <AuthProvider>
          <AppearanceProvider>
            <ThemeProvider>
              <NavigationConatiner />
            </ThemeProvider>
          </AppearanceProvider>
        </AuthProvider>
      </UserProvider>
    </DropDownComponentProvider>
  );
}

Then in any component

import { useDropDown } from "./location"

// Inside a functional component 

const { ref } = useDropDown()

// eg in use effect
   useEffect(() => {
    ref.current.alertWithType("success", "Log in successfull.", "asdasd");
  }, []);
blackwolf08 commented 4 years ago

I hope it helps :)

Lazymondaysunday commented 4 years ago

This seems to work, any idea how you would pass functions for onTap, onClose.

blackwolf08 commented 4 years ago

@Lazymondaysunday pass it in context, where i am passing the ref

Lazymondaysunday commented 4 years ago

@Lazymondaysunday pass it in context, where i am passing the ref

I'm trying to do it, but can't figure it out. Could you paste an example? Thanks!

pors commented 3 years ago

@Lazymondaysunday pass it in context, where i am passing the ref

I'm trying to do it, but can't figure it out. Could you paste an example? Thanks!

@blackwolf08 I'm also not sure how to do this. E.g. how do we set the closeInterval in the component where ref.current.alertWithType is called?

christhoval06 commented 2 years ago

useDropdownAlert.js

/**
 * @author: Christhoval Barba <christhoval@gmail.com>
 * @flow
 * @format
 */
import React, {createContext, useRef, useMemo, useState, useContext} from 'react';
import {DropdownAlert} from 'components/dropdownAlert';
import type {DropdownAlertType} from 'react-native-dropdownalert';

export type AlertWithTypeFn = (
  type: DropdownAlertType,
  title: string,
  message: string,
  payload?: {[string]: string},
  interval?: number,
) => void;

export type AlertContextType = {
  alertWithType: AlertWithTypeFn,
};

const AlertContext: React$Context<AlertContextType> = createContext<AlertContextType>({
  alertWithType() {},
});
const {Provider, Consumer} = AlertContext;

type React$Reference<T> = {current: React$ElementRef<T> | null};

export function DropdownAlertProvider({children}: {children: React$Node}): React$Element<typeof Provider> {
  const [alertType, setAlertType] = useState<string>('');
  const dropdown: React$Reference<DropdownAlertType> = useRef();
  const contextValue: AlertContextType = useMemo(
    () => ({
      alertWithType: (
        type: DropdownAlertType,
        title: string,
        message: string,
        payload?: {[string]: string},
        interval?: number,
      ) => {
        setAlertType(type);
        dropdown?.current?.alertWithType(type, title, message, payload, interval);
      },
    }),
    [dropdown],
  );
  return (
    <Provider value={contextValue}>
      {React.Children.only(children)}
      <DropdownAlert ref={dropdown} alertType={alertType} />
    </Provider>
  );
}

export function withAlert<T>(
  Component: React$ComponentType<AlertContextType & T>,
): React$StatelessFunctionalComponent<T> {
  return function WrapperComponent(props: T): React$Element<typeof Consumer> {
    return <Consumer>{(value: AlertContextType) => <Component {...props} {...value} />}</Consumer>;
  };
}

export const useDropdownAlert = (): AlertContextType => {
  const context = useContext<AlertContextType>(AlertContext);

  if (context === undefined) {
    throw new Error('useDropdownAlert was used outside of its Provider -> DropdownAlertProvider');
  }

  return context;
};

export default DropdownAlertProvider;

in App.js

...
return (
  <DropdownAlertProvider>
       ...
  </DropdownAlertProvider>
);
...

in Component

...
const Component = ({props}: Props) => {
    const {alertWithType} = useDropdownAlert();

    const showDropdown = useCallback(() => {
         ...
         alertWithType('error', 'title', 'message');
         ...
     }, [alertWithType]);

    ...
}
pors commented 2 years ago

@christhoval06 awesome, thanks a lot!

Do you maybe have a solution for the "alert behind modal" problem as well? It is a bit of a hassle to not use the provider for all modals.

christhoval06 commented 2 years ago

@pors current solution wrap Modal with AlertProvider and use hook.

Modals use different index or elevation, then alerts and other modal is behind

rajscet commented 2 years ago

Short Description

Improves Documentation

Steps to Reproduce / Code Snippets / Usage

import React, { useRef } from "react";
import DropdownAlert from "react-native-dropdownalert";

const DropDownContext = React.createContext();

export const DropDownComponentProvider = ({ children }) => {
  let ref = useRef();
  return (
    <DropDownContext.Provider
      value={{
        ref,
      }}
    >
      {children}
      <DropdownAlert ref={ref} />
    </DropDownContext.Provider>
  );
};

export const useDropDown = () => React.useContext(DropDownContext);

In App.js

import React from "react";
import { AppearanceProvider } from "react-native-appearance";
import {
  AuthProvider,
  DropDownComponentProvider,
  ThemeProvider,
  UserProvider,
} from "./contexts";
import { firebaseInit } from "./firebase";
import NavigationConatiner from "./navigation";

// init firebase app
firebaseInit();

export default function App() {

  return (
    <DropDownComponentProvider>
      <UserProvider>
        <AuthProvider>
          <AppearanceProvider>
            <ThemeProvider>
              <NavigationConatiner />
            </ThemeProvider>
          </AppearanceProvider>
        </AuthProvider>
      </UserProvider>
    </DropDownComponentProvider>
  );
}

Then in any component

import { useDropDown } from "./location"

// Inside a functional component 

const { ref } = useDropDown()

// eg in use effect
   useEffect(() => {
    ref.current.alertWithType("success", "Log in successfull.", "asdasd");
  }, []);

wow working great

vishaldhanotiyadev commented 2 years ago

This solution worked but it failed when I try to execute the Test case with jest.

How I Used

  const {alertRef} = useDropDownAlert();
  alertRef?.current?.alertWithType('info', 'Info', text);

● InfoIcon component › InfoIcon render with onClick

TypeError: Cannot read properties of undefined (reading 'alertRef')

  22 |   const icon = props.iconName || Icons.INFO_CIRCLE;
  23 |
> 24 |   const {alertRef} = useDropDownAlert();
     |          ^
  25 |
christhoval06 commented 6 months ago

@vishaldhanotiyadev you need provide a context provider to success test running.

Example

<DropdownAlertProvider>
... test
</DropdownAlertProvider >