{"id":22768593,"url":"https://github.com/verzsut/react-afc","last_synced_at":"2026-03-01T19:02:03.251Z","repository":{"id":48912625,"uuid":"517083800","full_name":"VerZsuT/react-afc","owner":"VerZsuT","description":"Allows you to significantly simplify the optimization of functional react components","archived":false,"fork":false,"pushed_at":"2023-12-19T18:29:39.000Z","size":266,"stargazers_count":10,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-27T11:29:57.009Z","etag":null,"topics":["constructor","function-component","react","redux"],"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/VerZsuT.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":"2022-07-23T14:45:00.000Z","updated_at":"2025-02-12T14:36:13.000Z","dependencies_parsed_at":"2024-12-11T14:12:13.506Z","dependency_job_id":"494d2271-7292-45e9-9dcc-167e88251b03","html_url":"https://github.com/VerZsuT/react-afc","commit_stats":{"total_commits":67,"total_committers":2,"mean_commits":33.5,"dds":0.02985074626865669,"last_synced_commit":"fb3f0ac12d263bc5227215391f8ce94c4d336db7"},"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"purl":"pkg:github/VerZsuT/react-afc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VerZsuT%2Freact-afc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VerZsuT%2Freact-afc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VerZsuT%2Freact-afc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VerZsuT%2Freact-afc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/VerZsuT","download_url":"https://codeload.github.com/VerZsuT/react-afc/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VerZsuT%2Freact-afc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29980795,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-01T16:35:47.903Z","status":"ssl_error","status_checked_at":"2026-03-01T16:35:44.899Z","response_time":124,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["constructor","function-component","react","redux"],"created_at":"2024-12-11T14:12:02.281Z","updated_at":"2026-03-01T19:02:03.235Z","avatar_url":"https://github.com/VerZsuT.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# React Advanced Function Component (AFC)\n\nAllows you to **simplify optimization**.\n\n_Full type support._\n\n# Table of contents\n\n**About the package**\n\n- [Installation](#installation)\n- [Why](#why)\n- [When to use](#when-to-use)\n- [What gives](#what-gives)\n- [Performance](#performance)\n- [Example](#example)\n- [Component structure](#component-structure)\n- [State management](#state-management)\n- [Working with context](#working-with-context)\n- [Regular hooks in constructor](#using-regular-hooks-in-the-body-of-the-constructor)\n- [Compatible with non-afc components](#compatible-with-non-afc-components)\n- [Common errors](#common-errors)\n- [API](#api)\n\n## Installation\n\n```npm\nnpm i react-afc\n```\n\n## When to use\n\nWhen you need to optimize a component by reducing the rerender of child components.\n\n## Why\n\nIn order not to write unnecessary `useMemo`, `useCallback` and `useRef`.\n\n## What gives\n\nAllows you to reduce the number of hook calls (which affects both readability and optimization),\nand also not to worry about an array of dependencies.\n\n## Performance\n\nThe library is optimized as much as possible.\n\n`afcMemo` returns the `memo`-component  \n`afc` returns a regular component\n\nEach render uses one `useRef` hook, and the `props` is updated (excluding the first render).\n\nCalling the following methods adds logic that is used during **each render**:\n\n- `useObjectState` /\n  `useReactive` /\n  `useState`\n  adds one **React.useState** call\n- `useRedux`\n  adds **ReactRedux.useSelector** calls depending on the passed object (one key - one hook call)\n- `useOnDestroy` /\n  `useOnMount` /\n  `useOnDraw` /\n  `useEffect` /\n  `useLayoutEffect`\n  adds one **React.useEffect** call with the passed callback\n- `useOnRender`\n  adds a call the passed callback (performance directly depends on the actions in it)\n- `useContext`\n  adds one **React.useContext** call\n- `useDispatch` /\n  `useActions`\n  adds one **ReactRedux.useDispatch** call\n- `useMemo`\n  adds one **React.useMemo** call\n\n_Note:_ All of them except `useRedux` / `useOnRender` / `useMemo` / `useContext` adds one hook call regardless of the number of its calls\n\nEach of the methods can be called an **unlimited** number of times, but only within the constructor\nand in functions called from it\n(exclution - methods from [_react-afc/compatible_](#compatible-with-non-afc-components)).\n\n## Example\n\n_See the description below_.\n\n```jsx\nimport { useState, afcMemo, useActions, useRedux, $ } from 'react-afc'\nimport { selectName, actions } from './store'\n\nfunction Component(props) {\n  const [getMultiplier, setMultiplier] = useState(2)\n  const [getNumber, setNumber] = useState(5)\n\n  const store = useRedux({\n    name: selectName\n  })\n\n  const { changeName } = useActions(actions)\n\n  function onChangeMult(event) {\n    setMultiplier(+event.currentTarget.value)\n  }\n  \n  function onChangeName(event) {\n    changeName(event.currentTarget.value)\n  }\n\n  function calcValue() {\n    return getMultiplier() * getNumber()\n  }\n\n  return () =\u003e \u003c\u003e\n    \u003ch1\u003eAdvanced function component\u003c/h1\u003e\n    \u003cinput value={store.name} onChange={onChangeName} /\u003e\n    \u003cinput value={getMultiplier()} onChange={onChangeMult} /\u003e\n    \u003cinput value={getNumber()} onChange={$(e =\u003e setNumber(+e.currentTarget.value))} /\u003e\n    \u003cp\u003eCalculated: {calcValue()}\u003c/p\u003e\n    \u003cp\u003eHi, {name}!\u003c/p\u003e\n  \u003c/\u003e\n}\n\nexport default afcMemo(Component)\n```\n\n## Component structure\n\n```jsx\nimport { afc } from 'react-afc'\n\nfunction Component(props) {\n  // The body of the \"constructor\".\n  // Is called once (before the first render).\n  // Common hooks must be wrapped or in 'useOnRender'.\n\n  return () =\u003e {\n    // Render function, as in a regular component.\n    // Every render is called.\n    return (\n      \u003cdiv\u003econtent\u003c/div\u003e\n    )\n  }\n}\n\nexport default afc(Component)\n```\n\n## State management\n\nTo work with the state, use\n`useReactive` /\n`useObjectState` /\n`useState`\n\n```jsx\nimport { afc, useObjectState, useReactive, useState } from 'react-afc'\n\nfunction Component(props) {\n  // useObjectState\n  const { state, setName, setSurname } = useObjectState({\n    name: 'Name',\n    surname: 'Surname'\n  })\n\n  function changeState1() {\n    setName('New name')\n    setSurname('New surname')\n  }\n\n  // useReactive\n  const reactive = useReactive({\n    name: 'Name',\n    surname: 'Surname'\n  })\n\n  function changeState2() {\n    reactive.name = 'New name'\n    reactive.surname = 'New surname'\n  }\n\n  // useState\n  const [getName, setName2] = useState('Name')\n  const [getSurname, setSurname2] = useState('Surname')\n\n  function changeState4() {\n    setName2('New name')\n    setSurname2('New surname')\n  }\n\n  return () =\u003e {/*...*/}\n}\n\nexport default afc(Component)\n```\n\nTo work with **Redux** use `useRedux` and `useDispatch` / `useActions`\n\n```jsx\nimport { afc, useRedux, useDispatch, useActions, $ } from 'react-afc'\nimport { actions } from './store'\nimport { changeCount, selectCount } from './countSlice'\n\nfunction Component(props) {\n  const store = useRedux({\n    name: s =\u003e s.name.current,\n    count: selectCount\n  })\n\n  function greet() {\n    return `Hi, ${store.name}!`\n  }\n\n  const dispatch = useDispatch()\n  \n  // Alternative\n  const { delCount } = useActions(actions)\n\n  return () =\u003e \u003c\u003e\n    \u003cinput onChange={$(e =\u003e dispatch(changeCount(+e.target.value)))}/\u003e\n    \u003cbutton onClick={$(() =\u003e delCount())}\u003e\n      Delete counter\n    \u003c/button\u003e\n  \u003c/\u003e\n}\n\nexport default afc(Component)\n```\n\n## Working with Context\n\nTo use the context, import the `useContext`.\n\n_Returns `contextGetter`, not the context itself_.\n\n```jsx\nimport { afc, useContext } from 'react-afc'\nimport CountContext from './CountContext'\n\nfunction Component(props) {\n  const getCount = useContext(CountContext)\n\n  function calculate() {\n    return getCount() * 5\n  }\n\n  return () =\u003e (\n    \u003cp\u003eCalculated: {calculate()}\u003c/p\u003e\n  )\n}\n\nexport default afc(Component)\n```\n\n## Using regular hooks in the body of the \"constructor\"\n\n```jsx\nimport { afc, useOnRender } from 'react-afc'\nimport { commonHook } from './hooks'\n\nfunction Component() {\n  let exampleVar = null\n\n  useOnRender(() =\u003e {\n    exampleVar = commonHook()\n  })\n\n  return () =\u003e {\n    // OR\n    // exampleVar = commonHook()\n    return (\n      \u003cp\u003eVariable: {exampleVar}\u003c/p\u003e\n    )\n  }\n}\n\nexport default afc(Component)\n```\n\nOr use `wrapStaticHook` / `wrapDynamicHook`\n\n```jsx\nimport { afc, useOnRender, useForceUpdate, wrapStaticHook, wrapDynamicHook } from 'react-afc'\n\nfunction commonHook(number) {\n  // any React common hooks\n}\n\n// If the result of the hook does not change\nconst staticHook = wrapStaticHook(commonHook)\n// Else\nconst dynamicHook = wrapDynamicHook(commonHook)\n\nfunction Component() {\n  let number = 5\n\n  const staticResult = staticHook(number)\n  const getDynamicResult = dynamicHook(() =\u003e [number])\n  const forceUpdate = useForceUpdate()\n\n  return () =\u003e {\n    number++\n\n    return \u003c\u003e\n      \u003cp\u003eStatic result: {staticResult}\u003c/p\u003e\n      \u003cp\u003eDynamic result: {getDynamicResult()}\u003c/p\u003e\n      \u003cbutton onClick={forceUpdate}\u003e\n        Force update\n      \u003c/button\u003e\n    \u003c/\u003e\n  }\n}\n\nexport default afc(Component)\n```\n\n`useOnRender` is called immediately and before each render (so as not to break hooks)\n\n```jsx\nimport { afc, useOnRender } from 'react-afc'\n\nfunction Component(props) {\n  console.log('Constructor start')\n  useOnRender(() =\u003e {\n    console.log('onRender')\n  })\n  console.log('After onRender')\n\n  return () =\u003e (\n    \u003cp\u003eonRender\u003c/p\u003e\n  )\n}\n\nexport default afc(Component)\n```\n\nIn this example, the console output will be:\n\n```text\nConstructor start\nonRender\nAfter onRender\n```\n\nAnd before each next render it will be output to the console\n\n```text\nonRender\n```\n\n## Compatible with non-afc components\n\nTo use the same code in regular and _afc_ components, use the methods from `react-afc/compatible`.\n\nThey have slightly less performance.\n\nWhen called in an _afc_ component, they work like normal methods for 'constructor'. When called in a regular component,\nthey use adapted versions that do not cause errors and do the same job as in 'constructor'.\n\n_Note:_ Use compatible methods only in reused functions.\nIn other cases, the fastest option will be the usual methods from `react-afc`.\n\n```jsx\nimport { afc, wrapDynamicHook } from 'react-afc'\nimport { useOnMount, useOnDestroy } from 'react-afc/compatible'\nimport { externalCommonHook } from './hooks'\n\nconst afcHook = wrapDynamicHook(externalCommonHook)\n\nfunction handleDocumentClick(callback) {\n  useOnMount(() =\u003e {\n    document.addEventListener('click', callback)\n  })\n  useOnDestroy(() =\u003e {\n    document.removeEventListener('click', callback)\n  })\n}\n\nconst AFCComponent = afc(props =\u003e {\n  handleDocumentClick(() =\u003e {\n    // any actions\n  })\n  afcHook(5)\n\n  return () =\u003e (\n    \u003cp\u003eafc component\u003c/p\u003e\n  )\n})\n\nfunction CommonComponent(props) {\n  // Will not cause errors\n  handleDocumentClick(() =\u003e {\n    // any actions\n  })\n  externalCommonHook(5)\n\n  return (\n    \u003cp\u003ecommon component\u003c/p\u003e\n  )\n}\n```\n\nFor single hard calculations use `useOnceCreated`\n\n## Common errors\n\nUnpacking at the declaration will break the updating of the props: `name` and `age` will be the same every render\n\n```ts\nimport { afc } from 'react-afc'\n\n                    // Error !!!\nfunction Component({ name, age }) {\n  // ...\n}\n\nexport default afc(Component)\n```\n\nUnpacking `state`, `props` or `reduxState` directly in the constructor body will **freeze** these variables:\n`name`, `age` and `surname` will not change between renders.\n\n_The exclusion is the case when the received fields do not change during the life of the component_  \n_Unpacking in **render function** or handlers does not have such a problem_\n\n```jsx\nimport { afc, useReactive, useRedux } from 'react-afc'\n\nfunction Component(props) {\n  const state = useReactive({\n    name: 'Aleksandr',\n    age: 20\n  })\n\n  const store = useRedux({\n    count: s =\u003e s.count.value\n  })\n\n  const { name, age } = state // Error, freeze !!!\n  const { count } = store\n  const { surname } = props\n\n  function onClick() {\n    const { name, age } = state // Right, always relevant\n    const { count } = store\n    const { surname } = props\n  }\n\n  return () =\u003e (\n    \u003cbutton onClick={onClick}\u003e\n      Click me\n    \u003c/button\u003e\n  )\n}\n\nexport default afc(Component)\n```\n\nIt is forbidden to use regular hooks in the constructor without the `useOnRender` wrapper.\n\nSince the \"constructor\" is called once, the call of the usual hooks in it will not be repeated in the render,\nwhich will cause the hooks to break and the application to crash.\n\nThe contents of `useOnRender` are called every render, which ensures that the hooks work correctly.\n\n_Note:_ Use `useOnRender` only when there is no other way.\n\n```jsx\nimport { useEffect as reactUseEffect } from 'react'\nimport { afc, useOnRender } from 'react-afc'\n\nfunction Component(props) {\n  reactUseEffect(/*...*/) // Error !!!\n\n  useOnRender(() =\u003e {\n    reactUseEffect(/*...*/) // Right\n  })\n\n  return () =\u003e {\n    reactUseEffect(/*...*/) // Right\n\n    return (\n      \u003cp\u003ecommon hooks\u003c/p\u003e\n    )\n  }\n}\n\nexport default afc(Component)\n```\n\n## API\n\nSee [Wiki](https://github.com/VerZsuT/react-afc/wiki)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fverzsut%2Freact-afc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fverzsut%2Freact-afc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fverzsut%2Freact-afc/lists"}