1 | # fuco
|
2 |
|
3 | [![npm](https://img.shields.io/npm/v/fuco.svg)](https://www.npmjs.com/package/fuco)
|
4 | [![install size](https://packagephobia.now.sh/badge?p=fuco)](https://packagephobia.now.sh/result?p=fuco)
|
5 | [![Build Status](https://travis-ci.com/wtnbass/fuco.svg?branch=master)](https://travis-ci.com/wtnbass/fuco)
|
6 | [![codecov](https://codecov.io/gh/wtnbass/fuco/branch/master/graph/badge.svg)](https://codecov.io/gh/wtnbass/fuco)
|
7 |
|
8 | Functional Component like React, but for Web Components.
|
9 |
|
10 | ```html
|
11 | <!DOCTYPE html>
|
12 | <html lang="en">
|
13 | <body>
|
14 | <counter-app></counter-app>
|
15 | <script type="module">
|
16 | import { html, defineElement, useState } from "//unpkg.com/fuco?module";
|
17 |
|
18 | function Counter() {
|
19 | const [count, setCount] = useState(0);
|
20 | return html`
|
21 | <div>${count}</div>
|
22 | <button @click=${() => setCount(count + 1)}>+</button>
|
23 | <button @click=${() => setCount(count - 1)}>-</button>
|
24 | `;
|
25 | }
|
26 |
|
27 | defineElement("counter-app", Counter);
|
28 | </script>
|
29 | </body>
|
30 | </html>
|
31 | ```
|
32 |
|
33 | - [Installation](#Installation)
|
34 | - [Hooks](#Hooks)
|
35 | - [useAttribute](#useAttribute)
|
36 | - [useProperty](#useProperty)
|
37 | - [useDispatchEvent](#useDispatchEvent)
|
38 | - [useStyle](#useStyle)
|
39 | - [useState](#useState)
|
40 | - [useReducer](#useReducer)
|
41 | - [useRef](#useRef)
|
42 | - [useContext](#useContext)
|
43 | - [useEffect](#useEffect)
|
44 | - [useMemo](#useMemo)
|
45 | - [useCallback](#useCallback)
|
46 |
|
47 | ## Installation
|
48 |
|
49 | ```sh
|
50 | npm install fuco
|
51 | # or use yarn
|
52 | yarn add fuco
|
53 | ```
|
54 |
|
55 | ## Hooks
|
56 |
|
57 | - Original Hooks
|
58 |
|
59 | - [useAttribute](#useAttribute)
|
60 | - [useProperty](#useProperty)
|
61 | - [useDispatchEvent](#useDispatchEvent)
|
62 | - [useStyle](#useStyle)
|
63 |
|
64 | - React Hooks compatible
|
65 |
|
66 | - [useState](#useState)
|
67 | - [useReducer](#useReducer)
|
68 | - [useRef](#useRef)
|
69 | - [useContext](#useContext)
|
70 | - [useEffect](#useEffect)
|
71 | - [useMemo](#useMemo)
|
72 | - [useCallback](#useCallback)
|
73 |
|
74 | ### useAttribute
|
75 |
|
76 | `useAttribute` returens attribute value, and updates the component when the attribute specified by the first argument is changed.
|
77 |
|
78 | ```js
|
79 | defineElement("greet-element", () => {
|
80 | const name = useAttribute("name");
|
81 | const hidden = useAttribute("hidden", value => value != null);
|
82 | if (hidden) {
|
83 | return html``;
|
84 | }
|
85 | return html`
|
86 | <div>Hello, ${name}</div>
|
87 | `;
|
88 | });
|
89 |
|
90 | html`
|
91 | <greet-element name="World"></greet-element>
|
92 | `;
|
93 | // => `<div>Hello, World</div>`
|
94 |
|
95 | html`
|
96 | <greet-element name="WebComponent" hidden></greet-element>
|
97 | `;
|
98 | // => ``
|
99 | ```
|
100 |
|
101 | ### useProperty
|
102 |
|
103 | `useProperty` returns element's property value, and updates the component when the property values is changed.
|
104 |
|
105 | ```js
|
106 | defineElement("card-element", () => {
|
107 | const card = useProperty("card");
|
108 | return html`
|
109 | <div>${card.mark} ${card.value}</div>
|
110 | `;
|
111 | });
|
112 |
|
113 | const heartAce = { mark: "♥", value: 1 };
|
114 | html`
|
115 | <card-element .card=${heartAce}></card-element>
|
116 | `;
|
117 | ```
|
118 |
|
119 | ### useDispatchEvent
|
120 |
|
121 | `useDispatchEvent` offers `dispatch` function like `dispatchEvent` to use CustomEvent.
|
122 |
|
123 | ```js
|
124 | defineElement("send-message", () => {
|
125 | const dispatch = useDispatchEvent("some-message", {
|
126 | bubbles: true,
|
127 | composed: true
|
128 | });
|
129 | return html`
|
130 | <button @click=${() => dispatch("Hi!")}>Send</button>
|
131 | `;
|
132 | });
|
133 |
|
134 | // You can listen custom event using `@` prefix.
|
135 | html`
|
136 | <send-message @some-message=${e => console.log(e.detail)}></send-message>
|
137 | `;
|
138 | ```
|
139 |
|
140 | ### useStyle
|
141 |
|
142 | `useStyle` can adapt a StyleSheet to the component.
|
143 |
|
144 | ```js
|
145 | function HelloWorld() {
|
146 | useStyle(
|
147 | () => css`
|
148 | div {
|
149 | color: red;
|
150 | }
|
151 | `
|
152 | );
|
153 | return html`
|
154 | <div>Hello, world</div>
|
155 | `;
|
156 | }
|
157 | ```
|
158 |
|
159 | ### useState
|
160 |
|
161 | `useState` returns a pair of state and setter function, and upadates the component by updating state using setter function.
|
162 |
|
163 | ```js
|
164 | function Counter() {
|
165 | const [count, setCount] = useState(0);
|
166 | return html`
|
167 | <div>${count}</div>
|
168 | <button @click=${() => setCount(count + 1)}>PLUS</button>
|
169 | `;
|
170 | }
|
171 | ```
|
172 |
|
173 | ### useReducer
|
174 |
|
175 | `useReducer` returns a pair of state and dispatch function.
|
176 |
|
177 | ```js
|
178 | function Counter() {
|
179 | const [count, dispatch] = useReducer((state, action) => state + action, 0);
|
180 | return html`
|
181 | <div>${count}</div>
|
182 | <button @click=${() => dispatch(1)}>PLUS</button>
|
183 | `;
|
184 | }
|
185 | ```
|
186 |
|
187 | ### useRef
|
188 |
|
189 | `useRef` returned a ref object like React's, and you can recieve a DOM by setting ref object to attribute.
|
190 |
|
191 | ```js
|
192 | function Input() {
|
193 | const [value, setValue] = useState("");
|
194 | const inputRef = useRef(null);
|
195 | return html`
|
196 | <input ref=${inputRef} />
|
197 | <button @click=${() => setValue(inputRef.current.value)}>push</button>
|
198 | `;
|
199 | }
|
200 | ```
|
201 |
|
202 | ### useContext
|
203 |
|
204 | `createContext` offers `Context`, and using`Context.defineProvider` to define provider, and you can consume it using `useContext(Context)`.
|
205 |
|
206 | ```js
|
207 | const ThemeContext = createContext();
|
208 |
|
209 | // define a custom element as Provider
|
210 | ThemeContext.defineProvider("theme-provider");
|
211 |
|
212 | const App = () => html`
|
213 | <theme-provider .value=${"dark"}>
|
214 | <theme-comsumer></theme-comsumer>
|
215 | </theme-provider>
|
216 | `;
|
217 |
|
218 | // consume context
|
219 | defineElement("theme-consumer", () => {
|
220 | const theme = useContext(ThemeContext);
|
221 | return html`
|
222 | <div>theme is ${theme}</div>
|
223 | `;
|
224 | });
|
225 | ```
|
226 |
|
227 | ### useEffect
|
228 |
|
229 | `useEffect` gives you a side effects. it will run after rendering the component.
|
230 |
|
231 | ```js
|
232 | function Timer() {
|
233 | useEffect(() => {
|
234 | const id = setInterval(() => console.log("interval"));
|
235 | return () => clearInterval(id);
|
236 | }, []);
|
237 | return html``;
|
238 | }
|
239 | ```
|
240 |
|
241 | ### useMemo
|
242 |
|
243 | `useMemo` returns a memorized value. the value will update when deps given as the second argument.
|
244 |
|
245 | ```js
|
246 | function Plus() {
|
247 | const value = useMemo(() => a + b, [a, b]);
|
248 | return html`
|
249 | ${value}
|
250 | `;
|
251 | }
|
252 | ```
|
253 |
|
254 | ### useCallback
|
255 |
|
256 | `useCallback` returns memorized callback as same as `useMemo`.
|
257 |
|
258 | ```js
|
259 | function Greet() {
|
260 | const greet = useCallback(() => alert("Hello"));
|
261 | return html`
|
262 | <button @click=${greet}>hello</button>
|
263 | `;
|
264 | }
|
265 | ```
|
266 |
|
267 | ## License
|
268 |
|
269 | MIT
|