Coder Social home page Coder Social logo

react-singleton-hook's People

Contributors

bglownia avatar dependabot-preview[bot] avatar dependabot[bot] avatar g07cha avatar kevinkhill avatar light-keeper avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

react-singleton-hook's Issues

Cant use singleton hook in "Routes" on Debug mode

Hi, i created my own custom hook and use react-singleton-hook to use it.

My App.js code:

const App: FC = () => (
  <NotifierWrapper>
    <QueryClientProvider client={queryClient}>
      <>
        <SingletonHooksContainer />
        <Routes />
      </>
    </QueryClientProvider>
  </NotifierWrapper>
)

My Routes.js component

const Routes: FC = () => {

  const { play, pause, loaded } = useMusic()

  const onStateChange = useCallback(() => {
    if (loaded && play && pause) {
      if (checkSceneHasMusic()) {
        play()
      } else {
        pause()
      }
    }
  }, [loaded, play, pause])

  return (
    <Router onStateChange={onStateChange}>
      <Lightbox>
        <Stack key='root'>
          <Scene key='Splash' component={Splash} hideNavBar initial />
          <Scene key='List' component={List} hideNavBar />
        </Stack>
      </Lightbox>
    </Router>
  )
}

export default Routes

My hook (i tried to reduce code to minimal but app crash too)

import { useCallback, useRef, useEffect, useState } from 'react'
import { singletonHook } from 'react-singleton-hook'
import { Player } from '@react-native-community/audio-toolkit'
import { checkSceneHasMusic } from 'utils'

const trackUrl = 'https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3'
const trackUrl2 = 'https://www.learningcontainer.com/wp-content/uploads/2020/02/Kalimba.mp3'

const mockFunction = () => {
  // MOCK
}

const init = { load: mockFunction, play: mockFunction, pause: mockFunction, paused: true, loaded: false }

type ReturnType = {
  load: (url: string) => void,
  play: () => void,
  pause: () => void,
  paused: boolean,
  loaded: boolean,
}

const useMusic = (): ReturnType => {
  const [paused, setPaused] = useState(false)
  const [loaded, setLoaded] = useState(false)

  useEffect(() => {
    if (!loaded) {
      setLoaded(true)
    }
  }, [loaded])

  return { load: mockFunction, play: mockFunction, pause: mockFunction, paused, loaded }
}

export default singletonHook(init, useMusic)

If i execute hook on "Routes" component app crash on iOS with this error:

2021-05-10 13:44:22.267632+0200 myapp[20448:251440] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff20421af6 __exceptionPreprocess + 242
	1   libobjc.A.dylib                     0x00007fff20177e78 objc_exception_throw + 48
	2   CoreFoundation                      0x00007fff2049e77f _CFThrowFormattedException + 194
	3   CoreFoundation                      0x00007fff2049c656 -[__NSArrayM insertObject:atIndex:].cold.2 + 0
	4   CoreFoundation                      0x00007fff2031f678 -[__NSArrayM insertObject:atIndex:] + 1134
	5   UIKitCore                           0x00007fff23f677ac -[UIViewController _addChildViewController:performHierarchyCheck:notifyWillMove:] + 571
	6   myapp                               0x000000010d6d220d -[RNSScreenContainerView attachScreen:atIndex:] + 125
	7   myapp                               0x000000010d6d2bd8 -[RNSScreenContainerView updateContainer] + 2168
	8   myapp                               0x000000010d6d39f0 __41-[RNSScreenContainerManager markUpdated:]_block_invoke + 352
	9   libdispatch.dylib                   0x000000011118c7ec _dispatch_call_block_and_release + 12
	10  libdispatch.dylib                   0x000000011118d9c8 _dispatch_client_callout + 8
	11  libdispatch.dylib                   0x000000011119be75 _dispatch_main_queue_callback_4CF + 1152
	12  CoreFoundation                      0x00007fff2038fdbb __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
	13  CoreFoundation                      0x00007fff2038a63e __CFRunLoopRun + 2685
	14  CoreFoundation                      0x00007fff203896d6 CFRunLoopRunSpecific + 567
	15  GraphicsServices                    0x00007fff2c257db3 GSEventRunModal + 139
	16  UIKitCore                           0x00007fff24696cf7 -[UIApplication _run] + 912
	17  UIKitCore                           0x00007fff2469bba8 UIApplicationMain + 101
	18  myapp                               0x000000010d11b630 main + 112
	19  libdyld.dylib                       0x00007fff2025a3e9 start + 1
	20  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
terminating with uncaught exception of type NSException
CoreSimulator 732.18.6 - Device: iPhone 12 (7E7D3327-1ACA-482F-B440-1302588C9173) - Runtime: iOS 14.4 (18D46) - DeviceType: iPhone 12

If i use on Scene components it works fine. And on iOS release mode only fail if i call some hook function (with same error that show above)

On Android not crash app but reload "Routes" if i call hook function, and music player not works if i used on "Routes". In Scenes components all work fines like on iOS

My versions

    "react": "17.0.1",
    "react-native": "0.64.0",
    "react-singleton-hook": "^3.1.1"

react-singleton-hook + use-context-selector

I have a utilty function that creates a Context.Provider, and a useContext.

For my specific case, I'd like to use use-context-selector library to improve the performance of my application.

The API for the use-context-selector is: const myContextData = useContextSelector(Context, (state) => state[0].data);
In my function, I create an abstraction for the useContextSelector, like this: (contextSelectorFn) => useContextSelector(Context, contextSelectorFn).

This way, I can use my custom hook directly like: const myUserId = useUserData((state) => state[0].id);.
I'm sort of having a hard time understand how could I simply adhere to this singleton pattern for this use case.

Here's the code for my utility function as of now:

import type { Dispatch, ReactElement, ReactNode, SetStateAction } from 'react';
import { useState } from 'react';
import { createContext, useContextSelector } from 'use-context-selector';

type GlobalState<State> = [State, Dispatch<SetStateAction<State>>];
type Provider = (props: { children: ReactNode }) => ReactElement;
type GlobalContext<State> = <Selected>(
  selector: (value: GlobalState<State>) => Selected,
) => Selected;

function createGlobalStateContext<State>(
  initialState: State,
): [GlobalContext<State>, Provider] {
  const Context = createContext<GlobalState<State>>([initialState, () => {}]);

  const ProviderComponent: Provider = ({ children }) => {
    const [state, setState] = useState<State>(initialState);

    const contextState: GlobalState<State> = [state, setState];

    return <Context.Provider value={contextState}>{children}</Context.Provider>;
  };

  return [
    (contextSelectorFn) => useContextSelector(Context, contextSelectorFn),
    ProviderComponent,
  ];
}

Expose resetLocalStateForTests() for use in user tests

I am using react-singleton-hook for some fairly complex behaviour and would like to have some jest tests. Unfortunately the global nature of the hook makes having multiple tests for it impossible.

When I have more than one test with the hook in it, I get this warning
"SingletonHooksContainer is mounted second time. You should mount SingletonHooksContainer before any other component and never unmount it.Alternatively, dont use SingletonHooksContainer it at all, we will handle that for you."

The second test also fails. Each test passes independently, so I suspect this global state is causing the problem.

In the tests for the container there is a function called resetLocalStateForTests that resets this global state that gets run after each test. Exposing this function for users of the hook would solve the problem as I would be able to run it after each test.

Please mount SingletonHooksContainer waring

Can not mount SingletonHooksContainer with react native.Please mount SingletonHooksContainer into your components tree manually. 
    at VideoPlayer (http://localhost:8081/index.bundle//&platform=android&dev=true&minify=false&app=com.example&modulesOnly=false&runModule=true:123441:22)
    at RCTView
    at View (http://localhost:8081/index.bundle//&platform=android&dev=true&minify=false&app=com.example&modulesOnly=false&runModule=true:59610:43)
    at App
    at RCTView
    at View (http://localhost:8081/index.bundle//&platform=android&dev=true&minify=false&app=com.example&modulesOnly=false&runModule=true:59610:43)
    at RCTView
    at View (http://localhost:8081/index.bundle//&platform=android&dev=true&minify=false&app=com.example&modulesOnly=false&runModule=true:59610:43)
    at AppContainer (http://localhost:8081/index.bundle//&platform=android&dev=true&minify=false&app=com.example&modulesOnly=false&runModule=true:59454:36)
    at example(RootComponent) (http://localhost:8081/index.bundle//&platform=android&dev=true&minify=false&app=com.example&modulesOnly=false&runModule=true:108811:28)

I have this waring but i don't know why

i use React Native 0.72.4

System:
  OS: Linux 6.2 Ubuntu 22.04.3 LTS 22.04.3 LTS (Jammy Jellyfish)
  CPU: (8) x64 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz
  Memory: 5.07 GB / 15.32 GB
  Shell:
    version: 5.8.1
    path: /usr/bin/zsh
Binaries:
  Node:
    version: 18.17.0
    path: ~/.nvm/versions/node/v18.17.0/bin/node
  Yarn:
    version: 1.22.19
    path: ~/.nvm/versions/node/v18.17.0/bin/yarn
  npm:
    version: 9.6.7
    path: ~/.nvm/versions/node/v18.17.0/bin/npm
  Watchman: Not Found
SDKs:
  Android SDK: Not Found
IDEs:
  Android Studio: AI-223.8836.35.2231.10671973
Languages:
  Java:
    version: 11.0.20.1
    path: /usr/bin/javac
  Ruby: Not Found
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.2.0
    wanted: 18.2.0
  react-native:
    installed: 0.72.4
    wanted: 0.72.4
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: Not found
  newArchEnabled: Not found

Incompatibility with React 18: ReactDOM.render is no longer supported in React 18

When trying to use react-singleton-hook with React 18 this error message comes up in the browser console:

react_devtools_backend.js:4026 Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot

overrideMethod @ react_devtools_backend.js:4026
printWarning @ react-dom.development.js:86
error @ react-dom.development.js:60
render @ react-dom.development.js:29572
mount @ env.js:14
addHook @ SingletonHooksContainer.js:59
(anonymous) @ singletonHook.js:44
commitHookEffectListMount @ react-dom.development.js:23049
commitPassiveMountOnFiber @ react-dom.development.js:24816
commitPassiveMountEffects_complete @ react-dom.development.js:24781
commitPassiveMountEffects_begin @ react-dom.development.js:24768
commitPassiveMountEffects @ react-dom.development.js:24756
flushPassiveEffectsImpl @ react-dom.development.js:26990
flushPassiveEffects @ react-dom.development.js:26935
performSyncWorkOnRoot @ react-dom.development.js:26032
flushSyncCallbacks @ react-dom.development.js:12009
commitRootImpl @ react-dom.development.js:26910
commitRoot @ react-dom.development.js:26638
finishConcurrentRender @ react-dom.development.js:25937
performConcurrentWorkOnRoot @ react-dom.development.js:25765
workLoop @ scheduler.development.js:266
flushWork @ scheduler.development.js:239
performWorkUntilDeadline @ scheduler.development.js:533

This is caused by this code segment in https://github.com/Light-Keeper/react-singleton-hook/blob/main/src/utils/env.js, specifically the render() call:

// ...

import { unstable_batchedUpdates, render } from 'react-dom';

// ...
export const mount = C => {
  if (globalObject.document && globalObject.document.createElement) {
    render(<C/>, globalObject.document.createElement('div'));
  } else {
    // ...
  }
};

How to pass arguments to react-singleton-hook?

Newbie question: Sometimes I need a hook that receive arguments in its declaration, something like:

const whatever = useMyCustomHook(withMyArgument)

How can I do it with react-singleton-hook?

I'm using version 3.4.0

Add option to unmount hook if there are no consumers

We use singleton hook to store data fetched through rest API and then updated via websocket. From performance perspective it will be better to unmount hood and unsubscribe from topic if there is no consumer, and create new hook instance if some component that uses hook will mount again.

How to cleanup between tests?

Using a singleton hook seems like a good solution for our particular need, but is causing some issues with testing. It appears that the singleton hook persists between tests, which is not ideal. I'd really like a way to destroy the hook upon test completion. There doesn't appear to be a way to do this?

How to use consume hooks with dynamic parameters?

import  { useEffect, useState } from 'react';
import { singletonHook } from 'react-singleton-hook';
const api = { async getMe(userId) { return { id: userId, name: 'test' }; } };

const init = { loading: true };

const useUserProfileImpl = (userId) => {
  const [profile, setProfile] = useState(init);
  useEffect(() => {
    api.getMe(userId)
      .then(profile => setProfile({ profile }))
      .catch(error => setProfile({ error }));
  }, []);

  return profile;
};

export const useUserProfile = singletonHook(init, useUserProfileImpl(userId));

Currently the consumed hook from singletonHook is just a pointer to the hook itself. I tried wrapping my hook around a function so that the parameter is curried, but it's no longer treated as a singletonHook.

Thanks!

Redux state is outdated in functions created inside a singleton-hook

Hello! We have a strange bug, where functions created inside a singleton-hook appear to be outdated. We have proved that the singleton-hook recognizes a Redux state update AND re-renders. But then, when one of the singleton-hook's functions is called by a consumer, it does not appear to be calling the most up-to-date version of the function.

I'm wondering if you can help? Do singleton-hooks memoize their outputs behind the scenes; or what else could be causing the singleton-hook to return a stale function?

We have confirmed that making this hook to NOT be a singleton-hook solves the problem. (see console.logs below)

Attached below is some example code (contains some pseudocode for simplicity)

const initialReduxState = {
  currentRound: undefined,
};

// ---

const useDriversRoundsPermissionsImpl = () => {
  const currentRound = useSelector(selectCurrentRound);

  const hasPermission = listenToIndexedDbChanges('hasPermission', currentRound?.id);

  useEffect(() => {
    if (currentRound) console.log(currentRound);
    // OUTPUT: { roundId: 309823, roundName: "Round 1" }
    // Prints 1st, suggesting a re-render isrequestPermissionForRound() should contain the updated state.
  }, [currentRound]);

  const requestPermissionForRound = async () => {
    const test = selectCurrentRound(store.getState());
    console.log(currentRound === undefined, test === undefined);
    // OUTPUT: true, false
    // When fetched with store.getState() the currentRound is NOT undefined.
    // OUTPUT WHEN NOT A SINGLETON-HOOK: false, false
  };

  return { hasPermission, requestPermissionForRound };
};

export const useDriversRoundsPermissions = singletonHook(
  {},
  useDriversRoundsPermissionsImpl
);

// ---

function RoundRequestManager() {
  const dispatch = useDispatch();  
  const currentRound = useSelector(selectCurrentRound);

  const { hasPermission, requestPermissionForRound } = useDriversRoundsPermissions();

  useEffect(() => {
    if (currentRound && !hasPermission) {
      requestPermissionForRound();
      // We can say with certainty that currentRound is NOT undefined when this is called
    }
  }, [hasPermission, currentRound]);

  return (
    <>
      <button
        onClick={() =>
          dispatch(setCurrentRound({ roundId: 309823, roundName: "Round 1" }))
        }
      >
        Join Round 1
      </button>

      <button
        onClick={() =>
          dispatch(setCurrentRound({ roundId: 981726, roundName: "Round 2" }))
        }
      >
        Join Round 2
      </button>
    </>
  );
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.