{"id":25764513,"url":"https://github.com/dorilahav/heksher","last_synced_at":"2026-04-18T14:04:53.497Z","repository":{"id":211790580,"uuid":"723453488","full_name":"dorilahav/heksher","owner":"dorilahav","description":"Heksher is like the cool, upgraded sibling of the regular React Context API","archived":false,"fork":false,"pushed_at":"2024-03-09T16:35:31.000Z","size":61,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-23T02:57:07.143Z","etag":null,"topics":["react","react-context","state","state-management"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dorilahav.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null}},"created_at":"2023-11-25T18:04:28.000Z","updated_at":"2023-12-10T20:22:31.000Z","dependencies_parsed_at":"2025-08-02T04:43:17.707Z","dependency_job_id":null,"html_url":"https://github.com/dorilahav/heksher","commit_stats":null,"previous_names":["dorilahav/heksher"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/dorilahav/heksher","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dorilahav%2Fheksher","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dorilahav%2Fheksher/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dorilahav%2Fheksher/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dorilahav%2Fheksher/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dorilahav","download_url":"https://codeload.github.com/dorilahav/heksher/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dorilahav%2Fheksher/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31971500,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T00:39:45.007Z","status":"online","status_checked_at":"2026-04-18T02:00:07.018Z","response_time":103,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["react","react-context","state","state-management"],"created_at":"2025-02-26T21:18:24.897Z","updated_at":"2026-04-18T14:04:53.451Z","avatar_url":"https://github.com/dorilahav.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eHeksher\u003c/h1\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n**Heksher** is like the cool, upgraded sibling of the regular React Context API. It only triggers re-renders when the stuff you're using actually changes.\n\n[![Bundle Size](https://badgen.net/bundlephobia/minzip/heksher)](https://bundlephobia.com/package/heksher)\n[![Latest Version](https://badgen.net/npm/v/heksher)](https://www.npmjs.com/package/heksher)\n\n\u003c/div\u003e\n\n## Installation\n```bash\nnpm install heksher # or yarn add heksher or pnpm add heksher\n```\n\nHeksher requires `react@16.9.0` or newer in order to work.\n\n## Getting Started\nLets suppose we are creating a game app and we want to tell our entire app the current time (which is fetched from the server).\n\nWe will start by creating a `CurrentTimeHeksher`:\n```typescript jsx\nimport {createHeksher} from 'heksher';\n\nconst CurrentTimeHeksher = createHeksher\u003cDate\u003e();\n```\nThen we will create our provider which will fetch the current time from the server and provide it to the app.\nTo do so, we will use the `.Provider` component that `createHeksher` gives us.\n```typescript jsx\nconst CurrentTimeProvider = ({children}: PropsWithChildren) =\u003e {\n  const [currentTime, setCurrentTime] = useState\u003cDate | null\u003e(null);\n  \n  useEffect(() =\u003e {\n    // Some time fetching mechanism\n  }, []);\n  \n  if (!currentTime) {\n    return \u003cCurrentTimeLoadingScreen/\u003e;\n  }\n  \n  return (\n    \u003cCurrentTimeHeksher.Provider value={currentTime}\u003e\n      {children}\n    \u003c/CurrentTimeHeksher.Provider\u003e\n  );\n}\n```\n\nNow that we have the component that provides us with the current time, we need a way to get it in our application.\nFor that we will use the `.use` hook the `createHeksher` function gives us.\n```typescript jsx\nconst useCurrentTime = CurrentTimeHeksher.use;\n```\nNow lets use our new Heksher:\n```typescript jsx\nconst Game = () =\u003e {\n  const currentTime = useCurrentTime();\n  \n  return (\n    \u003cdiv\u003e\n      \u003cTimeDisplay time={currentTime}/\u003e\n      {/* The rest of the game*/}\n    \u003c/div\u003e\n  )\n}\n\nconst App = () =\u003e (\n  \u003cCurrentTimeProvider\u003e\n    \u003cGame/\u003e\n  \u003c/CurrentTimeProvider\u003e\n);\n```\n\nLooks similar huh? Well, that's because **Heksher**'s API was inspired by the `ReactContextAPI`.\nYou might be asking yourself - why shouldn't I just use it then?\nLets answer that question together.\n\n## The Problem With React Context\nLets ask ChatGPT\n\n![The Problem With React Context](https://i.imgur.com/7FyP1Rm.jpg)\n\nWell... if you are wondering, that's still the case :P\n\nLets take this code as an example:\n```typescript jsx\ninterface TimerContextValue {\n  elapsedTime: number;\n  startTimer: () =\u003e void;\n}\n\nconst TimerContext = createContext\u003cTimerContextValue\u003e({} as TimerContextValue);\n\nconst TimerProvider = ({children}: PropsWithChildren) =\u003e {\n  const [elapsedTime, setElapsedTime] = useState(0);\n  \n  const startTimer = () =\u003e {\n    const startDate = Date.now();\n    \n    setInterval(() =\u003e {\n      setElapsedTime(Date.now() - startDate)\n    }, 100);\n  }\n  \n  return (\n    \u003cTimerContext.Provider value={{elapsedTime, startTimer}}\u003e\n      {children}\n    \u003c/TimerContext.Provider\u003e\n  )\n}\n\nconst useTimer = () =\u003e useContext(TimerContext);\n\nconst StartTimerButton = () =\u003e {\n  const {startTimer} = useTimer();\n  \n  return \u003cbutton onClick={startTimer}\u003eStart Timer\u003c/button\u003e\n}\n```\n\nHow often do you think `StartTimerButton` will re-render?\n\n1. One time only \n2. Every 100ms\n3. Every time the button is clicked\n\n...\n\nIf you guessed `2` you will be right. Even though `StartTimerButton` only uses the `startTimer` function that doesn't change, it will still be re-rendered every 100ms. \n\nLets try to optimize it:\n\n```typescript jsx\nconst TimerProvider = ({children}: PropsWithChildren) =\u003e {\n  const [elapsedTime, setElapsedTime] = useState(0);\n\n  const startTimer = useCallback(() =\u003e {\n    const startDate = Date.now();\n\n    setInterval(() =\u003e {\n      setElapsedTime(Date.now() - startDate)\n    }, 100);\n  }, []);\n\n  return (\n    \u003cTimerContext.Provider value={useMemo(() =\u003e ({elapsedTime, startTimer}), [elapsedTime])}\u003e\n      {children}\n    \u003c/TimerContext.Provider\u003e\n  )\n}\n```\n\nWe added a `useCallback` and a `useMemo` but we still get that re-render every `100ms` why is that?\nWell, React Context is not very sophisticated - when value changes, every `useContext` is re-rendered.\nEven with that `useMemo`, the value changes every time `elapsedTime` is changed and a re-render will occur.\n\nWhat can you do? **Use Heksher**\n\n## Solution\nLets take the same timer example from above but use **Heksher** instead:\n\n```typescript jsx\nimport {createHeksher} from 'heksher';\n\nconst TimerHeksher = createHeksher\u003cTimerHeksherValue\u003e();\n\nconst TimerProvider = ({children}: PropsWithChildren) =\u003e {\n  const [elapsedTime, setElapsedTime] = useState(0);\n\n  const startTimer = useCallback(() =\u003e {\n    const startDate = Date.now();\n\n    setInterval(() =\u003e {\n      setElapsedTime(Date.now() - startDate)\n    }, 100);\n  }, []);\n\n  return (\n    \u003cTimerHeksher.Provider value={{elapsedTime, startTimer}}\u003e\n      {children}\n    \u003c/TimerHeksher.Provider\u003e\n  )\n}\n\nconst StartTimerButton = () =\u003e {\n  const {startTimer} = TimerHeksher.use();\n\n  return \u003cbutton onClick={startTimer}\u003eStart Timer\u003c/button\u003e\n}\n```\n\nThats it! We now have an optimized heksher, using it anywhere in our app will only trigger re-renders on the fields we used.\n\nNow our `StartTimerButton` will only render once.\n\n## Best Practices\n### Memoization\nDid you notice the example up there?\nNo `useMemo` on the value object, kinda weird, huh? Well, **Heksher** handles that for us, no need to wrap the value with useMemo.\nBut don't forget to memoize all the fields inside the value, that's really important in order to minimize unneeded renders.\n\nLets see an example:\n```typescript jsx\ninterface RandomNumberHeksherValue {\n  number: number;\n  generateNewNumber: (min: number, max: number) =\u003e void;\n}\n\nconst RandomNumberHeksher = createHeksher\u003cRandomNumberHeksherValue\u003e();\n\nconst RandomNumberProvider = ({children}: PropsWithChildren) =\u003e {\n  const [number, setNumber] = useState(-1);\n  \n  const generateNewNumber = (min: number, max: number) =\u003e {\n    const randomNumber = Math.floor(Math.random() * (max - min)) + min;\n    \n    setNumber(randomNumber);\n  }\n\n  return (\n    \u003cRandomNumberHeksher.Provider value={{number, generateNewNumber}}\u003e\n      {children}\n    \u003c/RandomNumberHeksher.Provider\u003e\n  );\n}\n```\nNotice the issue?\n\nThe `generateNewNumber` function isn't getting memoized; a fresh function is made every time the provider renders, causing a re-render when `generateNewNumber` is used.\n\nTo fix this hiccup, we'll wrap the function with `useCallback` and feed it into the value object.\n\n```typescript jsx\nconst generateNewNumber = useCallback((min: number, max: number) =\u003e {\n  const randomNumber = Math.floor(Math.random() * (max - min)) + min;\n\n  setNumber(randomNumber);\n}, []);\n```\nThis is much better.\n\n### Using Objects Instead Of Tuples\nWe've all used `useState` before.\nI don't know about you, but one of the things that I really like about it is the fact that you can simply give the state value / set state function any name you want by simply destructuring an array.\n```typescript\nconst [name, setName] = useState('');\n```\nSooo easy!\n\nWell, if you'd want to do that with a `Heksher` you might want to wait a few versions until an official support is added.\nAs of right now, providing a `tuple` will re-render every usage of the `Heksher`, doesn't matter what field of it you accessed, that's in order to allow providing arrays.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdorilahav%2Fheksher","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdorilahav%2Fheksher","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdorilahav%2Fheksher/lists"}