{"id":13485125,"url":"https://github.com/wtnbass/fuco","last_synced_at":"2025-04-12T02:23:32.499Z","repository":{"id":40605265,"uuid":"177672911","full_name":"wtnbass/fuco","owner":"wtnbass","description":"Functional Component like React, but for Web Components.","archived":false,"fork":false,"pushed_at":"2023-01-07T14:17:26.000Z","size":2726,"stargazers_count":74,"open_issues_count":23,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-12T02:23:26.781Z","etag":null,"topics":["hooks","lit-html","webcomponents"],"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/wtnbass.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-03-25T22:16:34.000Z","updated_at":"2024-05-09T18:31:02.000Z","dependencies_parsed_at":"2023-02-07T09:45:59.019Z","dependency_job_id":null,"html_url":"https://github.com/wtnbass/fuco","commit_stats":null,"previous_names":["wtnbass/functional-web-component"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wtnbass%2Ffuco","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wtnbass%2Ffuco/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wtnbass%2Ffuco/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wtnbass%2Ffuco/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wtnbass","download_url":"https://codeload.github.com/wtnbass/fuco/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248506138,"owners_count":21115387,"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":["hooks","lit-html","webcomponents"],"created_at":"2024-07-31T17:01:46.882Z","updated_at":"2025-04-12T02:23:32.477Z","avatar_url":"https://github.com/wtnbass.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# fuco\n\n[![npm](https://img.shields.io/npm/v/fuco.svg)](https://www.npmjs.com/package/fuco)\n[![install size](https://packagephobia.now.sh/badge?p=fuco)](https://packagephobia.now.sh/result?p=fuco)\n[![test](https://github.com/wtnbass/fuco/workflows/test/badge.svg)](https://github.com/wtnbass/fuco/actions)\n[![codecov](https://codecov.io/gh/wtnbass/fuco/branch/master/graph/badge.svg)](https://codecov.io/gh/wtnbass/fuco)\n\nFunctional Component like React, but for Web Components.\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n  \u003cbody\u003e\n    \u003ccounter-app\u003e\u003c/counter-app\u003e\n    \u003cscript type=\"module\"\u003e\n      import { html, defineElement, useState } from \"//unpkg.com/fuco?module\";\n\n      function Counter() {\n        const [count, setCount] = useState(0);\n        return html`\n          \u003cdiv\u003e${count}\u003c/div\u003e\n          \u003cbutton @click=${() =\u003e setCount(count + 1)}\u003e+\u003c/button\u003e\n          \u003cbutton @click=${() =\u003e setCount(count - 1)}\u003e-\u003c/button\u003e\n        `;\n      }\n\n      defineElement(\"counter-app\", Counter);\n    \u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n- [Installation](#Installation)\n- [Hooks](#Hooks)\n  - [useAttribute](#useAttribute)\n  - [useProperty](#useProperty)\n  - [useDispatchEvent](#useDispatchEvent)\n  - [useStyle](#useStyle)\n  - [useState](#useState)\n  - [useReducer](#useReducer)\n  - [useRef](#useRef)\n  - [useContext](#useContext)\n  - [useEffect](#useEffect)\n  - [useMemo](#useMemo)\n  - [useCallback](#useCallback)\n\n## Installation\n\n```sh\nnpm install fuco\n# or use yarn\nyarn add fuco\n```\n\n## Hooks\n\n- Original Hooks\n\n  - [useAttribute](#useAttribute)\n  - [useProperty](#useProperty)\n  - [useDispatchEvent](#useDispatchEvent)\n  - [useStyle](#useStyle)\n\n- React Hooks compatible\n\n  - [useState](#useState)\n  - [useReducer](#useReducer)\n  - [useRef](#useRef)\n  - [useContext](#useContext)\n  - [useEffect](#useEffect)\n  - [useLayoutEffect](#useLayoutEffect)\n  - [useMemo](#useMemo)\n  - [useCallback](#useCallback)\n\n### useAttribute\n\n`useAttribute` returens attribute value, and updates the component when the attribute specified by the first argument is changed.\n\n```js\ndefineElement(\"greet-element\", () =\u003e {\n  const name = useAttribute(\"name\");\n  const hidden = useAttribute(\"hidden\", value =\u003e value != null);\n  if (hidden) {\n    return html``;\n  }\n  return html`\n    \u003cdiv\u003eHello, ${name}\u003c/div\u003e\n  `;\n});\n\nhtml`\n  \u003cgreet-element name=\"World\"\u003e\u003c/greet-element\u003e\n`;\n// =\u003e `\u003cdiv\u003eHello, World\u003c/div\u003e`\n\nhtml`\n  \u003cgreet-element name=\"WebComponent\" hidden\u003e\u003c/greet-element\u003e\n`;\n// =\u003e ``\n```\n\n### useProperty\n\n`useProperty` returns element's property value, and updates the component when the property values is changed.\n\n```js\ndefineElement(\"card-element\", () =\u003e {\n  const card = useProperty(\"card\");\n  return html`\n    \u003cdiv\u003e${card.mark} ${card.value}\u003c/div\u003e\n  `;\n});\n\nconst heartAce = { mark: \"♥\", value: 1 };\nhtml`\n  \u003ccard-element .card=${heartAce}\u003e\u003c/card-element\u003e\n`;\n```\n\n### useDispatchEvent\n\n`useDispatchEvent` offers `dispatch` function like `dispatchEvent` to use CustomEvent.\n\n```js\ndefineElement(\"send-message\", () =\u003e {\n  const dispatch = useDispatchEvent(\"some-message\", {\n    bubbles: true,\n    composed: true\n  });\n  return html`\n    \u003cbutton @click=${() =\u003e dispatch(\"Hi!\")}\u003eSend\u003c/button\u003e\n  `;\n});\n\n// You can listen custom event using `@` prefix.\nhtml`\n  \u003csend-message @some-message=${e =\u003e console.log(e.detail)}\u003e\u003c/send-message\u003e\n`;\n```\n\n### useStyle\n\n`useStyle` can adapt a StyleSheet to the component.\n\n```js\nfunction HelloWorld() {\n  useStyle(\n    () =\u003e css`\n      div {\n        color: red;\n      }\n    `\n  );\n  return html`\n    \u003cdiv\u003eHello, world\u003c/div\u003e\n  `;\n}\n```\n\n### useState\n\n`useState` returns a pair of state and setter function, and upadates the component by updating state using setter function.\n\n```js\nfunction Counter() {\n  const [count, setCount] = useState(0);\n  return html`\n    \u003cdiv\u003e${count}\u003c/div\u003e\n    \u003cbutton @click=${() =\u003e setCount(count + 1)}\u003ePLUS\u003c/button\u003e\n  `;\n}\n```\n\n### useReducer\n\n`useReducer` returns a pair of state and dispatch function.\n\n```js\nfunction Counter() {\n  const [count, dispatch] = useReducer((state, action) =\u003e state + action, 0);\n  return html`\n    \u003cdiv\u003e${count}\u003c/div\u003e\n    \u003cbutton @click=${() =\u003e dispatch(1)}\u003ePLUS\u003c/button\u003e\n  `;\n}\n```\n\n### useRef\n\n`useRef` returned a ref object like React's, and you can recieve a DOM by setting ref object to attribute.\n\n```js\nfunction Input() {\n  const [value, setValue] = useState(\"\");\n  const inputRef = useRef(null);\n  return html`\n    \u003cinput :ref=${inputRef} /\u003e\n    \u003cbutton @click=${() =\u003e setValue(inputRef.current.value)}\u003epush\u003c/button\u003e\n  `;\n}\n```\n\n### useContext\n\n`createContext` offers `Context`, and using`Context.defineProvider` to define provider, and you can consume it using `useContext(Context)`.\n\n```js\nconst ThemeContext = createContext();\n\n// define a custom element as Provider\nThemeContext.defineProvider(\"theme-provider\");\n\nconst App = () =\u003e html`\n  \u003ctheme-provider .value=${\"dark\"}\u003e\n    \u003ctheme-comsumer\u003e\u003c/theme-comsumer\u003e\n  \u003c/theme-provider\u003e\n`;\n\n// consume context\ndefineElement(\"theme-consumer\", () =\u003e {\n  const theme = useContext(ThemeContext);\n  return html`\n    \u003cdiv\u003etheme is ${theme}\u003c/div\u003e\n  `;\n});\n```\n\n### useEffect\n\n`useEffect` gives you a side effects. it will run after rendering the component.\n\n```js\nfunction Timer() {\n  useEffect(() =\u003e {\n    const id = setInterval(() =\u003e console.log(\"interval\"));\n    return () =\u003e clearInterval(id);\n  }, []);\n  return html``;\n}\n```\n\n### useLayoutEffect\n\n`useLayoutEffect` runs after the DOM has been updated, but before the browser has had a chance to paint those changes\n\n```js\nfunction Box() {\n  const ref = useRef(null);\n  useLayoutEffect(() =\u003e {\n    ref.current.style.top = \"100px\";\n  });\n  return html`\n    \u003cdiv :ref=${ref}\u003e\u003c/div\u003e\n  `;\n}\n```\n\n### useMemo\n\n`useMemo` returns a memorized value. the value will update when deps given as the second argument.\n\n```js\nfunction Plus() {\n  const value = useMemo(() =\u003e a + b, [a, b]);\n  return html`\n    ${value}\n  `;\n}\n```\n\n### useCallback\n\n`useCallback` returns memorized callback as same as `useMemo`.\n\n```js\nfunction Greet() {\n  const greet = useCallback(() =\u003e alert(\"Hello\"));\n  return html`\n    \u003cbutton @click=${greet}\u003ehello\u003c/button\u003e\n  `;\n}\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwtnbass%2Ffuco","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwtnbass%2Ffuco","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwtnbass%2Ffuco/lists"}