{"id":23248244,"url":"https://github.com/devhammed/use-global-hook","last_synced_at":"2025-08-20T07:31:12.085Z","repository":{"id":35011651,"uuid":"196359573","full_name":"devhammed/use-global-hook","owner":"devhammed","description":"Painless global state management for React using Hooks and Context API in 1KB!","archived":false,"fork":false,"pushed_at":"2024-06-14T22:38:18.000Z","size":2066,"stargazers_count":57,"open_issues_count":4,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-11-29T11:55:34.581Z","etag":null,"topics":["context-api","global-hooks","hook-store","react","react-hooks"],"latest_commit_sha":null,"homepage":"https://hammed.dev/use-global-hook/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devhammed.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-07-11T09:07:37.000Z","updated_at":"2024-06-14T22:38:21.000Z","dependencies_parsed_at":"2024-06-14T23:47:59.942Z","dependency_job_id":"a95debb8-20f7-45ae-8786-d7f751978961","html_url":"https://github.com/devhammed/use-global-hook","commit_stats":{"total_commits":52,"total_committers":2,"mean_commits":26.0,"dds":"0.13461538461538458","last_synced_commit":"3e1e16ca97496e3810647956d0967296926a63a9"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devhammed%2Fuse-global-hook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devhammed%2Fuse-global-hook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devhammed%2Fuse-global-hook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devhammed%2Fuse-global-hook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devhammed","download_url":"https://codeload.github.com/devhammed/use-global-hook/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230400628,"owners_count":18219832,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["context-api","global-hooks","hook-store","react","react-hooks"],"created_at":"2024-12-19T08:12:57.280Z","updated_at":"2024-12-19T08:12:57.828Z","avatar_url":"https://github.com/devhammed.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# use-global-hook\n\n\u003e Painless global state management for React using Hooks and Context API in 1KB!\n\n[![NPM](https://img.shields.io/npm/v/@devhammed/use-global-hook.svg)](https://www.npmjs.com/package/@devhammed/use-global-hook) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) [![Made in Nigeria](https://img.shields.io/badge/made%20in-nigeria-008751.svg?style=flat-square)](https://github.com/acekyd/made-in-nigeria)\n\n## Installation\n\n```sh\nnpm install @devhammed/use-global-hook\n```\n\n## Quick Example\n\n```jsx\nimport React from 'react'\nimport ReactDOM from 'react-dom'\nimport { GlobalHooksProvider, createGlobalHook, useGlobalHook } from '@devhammed/use-global-hook'\n\nconst store = createGlobalHook(/** 1 **/ 'counterStore', () =\u003e {\n  const [count, setCount] = React.useState(0)\n\n  const increment = () =\u003e setCount(count + 1)\n  const decrement = () =\u003e setCount(count - 1)\n  const reset = () =\u003e setCount(0)\n\n  return { count, increment, decrement, reset }\n})\n\nfunction Counter () {\n  const { count, increment, decrement, reset } = useGlobalHook('counterStore') /** 1. This is where you use the name you defined in `createGlobalHook` function, this name should be unique through out your app **/\n\n  return (\n    \u003cdiv\u003e\n      \u003cbutton onClick={decrement}\u003e-\u003c/button\u003e\n      \u003cspan\u003e{count}\u003c/span\u003e\n      \u003cbutton onClick={increment}\u003e+\u003c/button\u003e\n      \u003cbutton onClick={reset}\u003ereset\u003c/button\u003e\n    \u003c/div\u003e\n  )\n}\n\nfunction App () {\n  return (\n    \u003cGlobalHooksProvider hooks={[ store ]}\u003e\n      \u003cCounter /\u003e\n      \u003cCounter /\u003e\n      \u003cCounter /\u003e\n    \u003c/GlobalHooksProvider\u003e\n  )\n}\n\nReactDOM.render(\u003cApp /\u003e, document.getElementById('root'))\n```\n\nNotice how we render the `Counter` component three times? clicking on a button in one of the component instance will update others too.\n\nSee it running live [here](https://devhammed.github.io/use-global-hook).\n\n### Concepts\n\nuse-global-hook is built on top of React Hooks and Context API. I first used this concept in a project at my workplace [Epower](https://epower.ng) and seeing the re-usability and convenience, I decided to convert it to a standalone open-source library for others to benefit from the awesomeness of React Hooks.\n\n##### `Store`\n\nA hook store is a place to store state and some of the logic for updating it.\n\nStore is a very simple React hook wrapper (which means you can re-use it, use other hooks within it, etc).\n\n`createGlobalHook` is a Hook store function wrapper, this function is used to apply some internally used property to a function that calls your original hook function. A wrapper function is best for this case as it is not a good practice to mutate your original function with properties that may conflict and third-party hooks is taking into consideration where it is not good to add properties to the library core exports and this method also allows creating clone of same hook function without conflicting instances.\n\nWrapping the function means, in case of when creating dynamic hook function, any argument you intend to pass to your hook when will be applied automatically, you still have your function the way you declare it and the way you intend to use it --- cheers! e.g  something like `store(props.dynamicValue)` though this can only happen when registering the hook function in `\u003cGlobalHooksProvider /\u003e`.\n\n```js\nimport React from 'React'\nimport { createGlobalHook } from '@devhammed/use-global-hook'\n\nconst store = createGlobalHook('counterStore', () =\u003e {\n  const [count, setCount] = React.useState(0)\n\n  const increment = () =\u003e setCount(count + 1)\n  const decrement = () =\u003e setCount(count - 1)\n  const reset = () =\u003e setCount(0)\n\n  return { count, increment, decrement, reset }\n})\n```\n\nThis example uses the official React `useState()` hook, but you are not limited to this only, there are other hooks like `useReducer()` if you need something like Redux or any custom or third-party hook as far as it follows the Rules of Hooks, you can read more on Hooks on React official website [here](https://reactjs.org/docs/hooks-intro.html).\n\n##### `useGlobalHook`\n\nNext we'll need a piece to introduce our state back into the tree so that:\n\n- When state changes, our components re-render.\n- We can depend on our store state.\n- We can call functions exposed by the store.\n\nFor this we have the `useGlobalHook` hook which allows us to get global store instances by using passing the value we used when creating the global hook with `createGlobalHook` function.\n\n```jsx\nfunction Counter () {\n  const { count, decrement, increment } = useGlobalHook('counterStore')\n\n  return (\n    \u003cdiv\u003e\n      \u003cspan\u003e{count}\u003c/span\u003e\n      \u003cbutton onClick={decrement}\u003e-\u003c/button\u003e\n      \u003cbutton onClick={increment}\u003e+\u003c/button\u003e\n    \u003c/div\u003e\n  )\n}\n```\n\n##### `\u003cGlobalHooksProvider\u003e`\nThe `\u003cdiv\u003e` component has two roles:\n\n1. It initializes global instances of given hooks array (an Array is required because React expects the number of hooks to be consistent across re-renders and Objects are not guaranteed to return in same order)\n2. It uses context to pass initialized instances of given stores to all the components down the tree.\n\n```jsx\nReactDOM.render(\n  \u003cGlobalHooksProvider hooks={[ counterStore ]}\u003e\n    \u003cCounter /\u003e\n  \u003c/GlobalHooksProvider\u003e\n);\n```\n\n### Nesting Providers\nuse-global-hook supports nesting `GlobalHooksProvider` which means child components can have their own global state and still be able to access their parent or root global state. Take a look at below example, explanation comes after it.\n\n```js\n  import React from 'react'\n  import ReactDOM from 'react-dom'\n  import {\n    GlobalHooksProvider,\n    useGlobalHook,\n    createGlobalHook\n  } from '@devhammed/use-global-hook'\n\n  const counterStoreHook = createGlobalHook('counterStore', () =\u003e {\n    const [count, setCount] = React.useState(0)\n\n    const increment = () =\u003e setCount(count + 1)\n    const decrement = () =\u003e setCount(count - 1)\n    const reset = () =\u003e setCount(0)\n\n    return { count, increment, decrement, reset }\n  })\n\n  const timerHook = createGlobalHook('timerStore', () =\u003e {\n    const [time, setTime] = React.useState(new Date())\n\n    React.useEffect(() =\u003e {\n      const id = setInterval(() =\u003e setTime(new Date()), 1000)\n      return () =\u003e clearInterval(id)\n    }, [])\n\n    return time\n  })\n\n  function Time () {\n    const time = useGlobalHook('timerStore')\n    const { count } = useGlobalHook('counterStore')\n\n    return \u003cp\u003e{time.toString()} - {count}\u003c/p\u003e\n  }\n\n  function Timer () {\n    return (\n      \u003cGlobalHooksProvider hooks={[ timerHook ]}\u003e\n        \u003cTime /\u003e\n        \u003cTime /\u003e\n        \u003cTime /\u003e\n      \u003c/GlobalHooksProvider\u003e\n    )\n  }\n\n  function Counter () {\n    const { count, increment, decrement, reset } = useGlobalHook('counterStore')\n\n    return (\n      \u003cdiv\u003e\n        \u003cbutton onClick={decrement}\u003e-\u003c/button\u003e\n        \u003cspan\u003e{count}\u003c/span\u003e\n        \u003cbutton onClick={increment}\u003e+\u003c/button\u003e\n        \u003cbutton onClick={reset}\u003ereset\u003c/button\u003e\n      \u003c/div\u003e\n    )\n  }\n\n  function App () {\n    return (\n      \u003cGlobalHooksProvider hooks={[ counterStoreHook ]}\u003e\n        \u003cTimer /\u003e\n        \u003cCounter /\u003e\n        \u003cCounter /\u003e\n        \u003cCounter /\u003e\n      \u003c/GlobalHooksProvider\u003e\n    )\n  }\n\n  ReactDOM.render(\u003cApp /\u003e, document.getElementById('root'))\n```\n\n[View Demo](https://6fhi2.codesandbox.io)\n\nSee how the `Time` components wrapped by `Timer` are able to access both `counterStore` and `timerStore`?\nWhen you render `GlobalHooksProvider`, one of the things it does under the hood is to try to use it parent global context, If it is undefined this means that it is the root else it merges with the parent context and you are able to access any store hook from parent(s) but the parent cannot access child nested global state because data flows in one direction else you define a function in global state that will communicate to other component.\n\n### Class Components\nYou heard that right, who says class component cannot use and benefit from the awesomeness of React hooks?\nuse-global-hooks provides a function component HOC wrapper `withGlobalHooks` which allows class components to use hooks state(s) by passing them props. cool right? let's look at how above Counter component will look when using a class.\n\n```js\n  // Counter.js\n\n  import { withGlobalHooks } from '@devhammed/use-global-hook'\n\n  class Counter extends React.Component {\n    render () {\n      const { count, increment, decrement, reset } = this.props.counterStore\n\n      return (\n        \u003cdiv\u003e\n          \u003cbutton onClick={decrement}\u003e-\u003c/button\u003e\n          \u003cspan\u003e{count}\u003c/span\u003e\n          \u003cbutton onClick={increment}\u003e+\u003c/button\u003e\n          \u003cbutton onClick={reset}\u003ereset\u003c/button\u003e\n        \u003c/div\u003e\n      )\n    }\n  }\n\n  export default withGlobalHooks(Counter, ['counterStore'])\n```\n\nIt is as easy as using function component too, just pass in your component variable as the first parameter and second parameter is an array that contains names of the global hooks you want to use and you will be able access the state from `this.props.[globalHookName]`. The HOC wrapper pass down props so any other prop you are using in your component still works fine except if there is prop conflict which is why it is recommended you add `Store` suffix to your store names when creating them.\n\nSo with support for class component, you can start using this library even when you are not ready to switch to function components.\n\nNOTE: You can also use `withGlobalHooks` with function components but why not just use the hook? :wink:\n\n### Testing\n\nGlobal Hooks are just your regular hooks too, so you can easily test with `react-hooks-testing-library` library e.g\n\n```js\nimport { renderHook, act } from 'react-hooks-testing-library'\n\ntest('counter', async () =\u003e {\n  let count, increment, decrement\n  renderHook(() =\u003e ({count, increment, decrement} = counterStore()))\n\n  expect(count).toBe(0)\n\n  act(() =\u003e increment())\n  expect(count).toBe(1)\n\n  act(() =\u003e decrement())\n  expect(count).toBe(0)\n})\n```\n\n### Pro-Tip\n\nCreate a file like `storeNames.{js,ts}` that contains names of your stores so you can re-use the strings to avoid making mistake when typing or when refactoring so you will have to change the names in one place, see example below:\n\n```js\n// utils/storeNames.js\n\nexport const API_STORE = 'apiStore'\n\nexport const COUNTER_STORE = 'counterStore'\n```\n\nThen you can create your store like this...\n\n```js\n// utils/mainStore.js\n\nimport { COUNTER_STORE } from '../utils/storeNames.js'\n\nconst counterStoreHook = createGlobalHook(COUNTER_STORE, () =\u003e {\n    const [count, setCount] = React.useState(0)\n\n    const increment = () =\u003e setCount(count + 1)\n    const decrement = () =\u003e setCount(count - 1)\n    const reset = () =\u003e setCount(0)\n\n    return { count, increment, decrement, reset }\n  })\n\n```\n\nThen register in your root component and you use it anywhere like this:\n\n```jsx\n// pages/counter.js\n\nimport { COUNTER_STORE } from '../utils/storeNames.js'\n\nfunction Counter () {\n    const { count, increment, decrement, reset } = useGlobalHook(COUNTER_STORE)\n\n    return (\n      \u003cdiv\u003e\n        \u003cbutton onClick={decrement}\u003e-\u003c/button\u003e\n        \u003cspan\u003e{count}\u003c/span\u003e\n        \u003cbutton onClick={increment}\u003e+\u003c/button\u003e\n        \u003cbutton onClick={reset}\u003ereset\u003c/button\u003e\n      \u003c/div\u003e\n    )\n  }\n```\n\nNice and Clean!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevhammed%2Fuse-global-hook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevhammed%2Fuse-global-hook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevhammed%2Fuse-global-hook/lists"}