├── useKeyPress.js
├── useWindowHeight.js
├── useWindowWidth.js
├── useDocumentTitle.js
├── useMedia.js
├── useDOMState.js
├── useKeyLog.js
├── useMousePosition.js
├── useToggle.js
├── useFormInput.js
├── useStuck.js
├── useClickOutside.js
├── useImmer.js
├── useEvent.js
├── useInterval.js
├── useDelay.js
├── useFirebase.js
├── useRouter.js
├── useIsOnline.js
├── useScrollDetection.js
├── usePromise.js
├── useFetch.js
├── useGeolocation.js
├── useBroadcast.js
├── useStateListener.js
├── useStorage.js
├── useData.js
├── LICENSE
└── useForm.js
/useKeyPress.js:
--------------------------------------------------------------------------------
1 | import useEvent from "./useEvent";
2 |
3 | export default function useKeyPress(handler) {
4 | useEvent("keypress", handler, document);
5 | };
6 |
--------------------------------------------------------------------------------
/useWindowHeight.js:
--------------------------------------------------------------------------------
1 | import useStateListener from "./useStateListener";
2 |
3 | export default function useWindowSize() {
4 | return useStateListener("resize", () => innerHeight).state;
5 | };
6 |
--------------------------------------------------------------------------------
/useWindowWidth.js:
--------------------------------------------------------------------------------
1 | import useStateListener from "./useStateListener";
2 |
3 | export default function useWindowSize() {
4 | return useStateListener("resize", () => innerWidth).state;
5 | }
6 |
--------------------------------------------------------------------------------
/useDocumentTitle.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 |
3 | export default function useDocumentTitle(title) {
4 | useEffect(() => {
5 | document.title = title;
6 | }, [title]);
7 | };
8 |
--------------------------------------------------------------------------------
/useMedia.js:
--------------------------------------------------------------------------------
1 | import useStateListener from "./useStateListener";
2 |
3 | export default function useMedia(query) {
4 | return useStateListener("resize", () => matchMedia(query).matches).state;
5 | }
6 |
--------------------------------------------------------------------------------
/useDOMState.js:
--------------------------------------------------------------------------------
1 | import useStateListener from "./useStateListener";
2 |
3 | export default function useDOMState() {
4 | return useStateListener("DOMContentLoaded", () => document.readyState).state;
5 | };
6 |
--------------------------------------------------------------------------------
/useKeyLog.js:
--------------------------------------------------------------------------------
1 | import useStateListener from "./useStateListener";
2 |
3 | export default function useKeyLog(handler) {
4 | return useStateListener("keypress", (e, state) => [e.key, ...state], document)
5 | .state;
6 | }
7 |
--------------------------------------------------------------------------------
/useMousePosition.js:
--------------------------------------------------------------------------------
1 | import useStateListener from "./useStateListener";
2 |
3 | export const useMousePosition = () => {
4 | return useStateListener("mousemove", e => ({
5 | x: e && e.clientX,
6 | y: e && e.clientY
7 | })).state;
8 | };
9 |
--------------------------------------------------------------------------------
/useToggle.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | export default function useToggle(initialValue = false) {
4 | const [state, setState] = useState(initialValue);
5 | const toggle = () => setState(!state);
6 | return [state, toggle, setState];
7 | };
8 |
--------------------------------------------------------------------------------
/useFormInput.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | export default function useFormInput(initalValue) {
4 | const [value, setValue] = useState(initalValue);
5 | const onChange = e => setValue(e.currentTarget.value);
6 | return { value, onChange };
7 | };
8 |
--------------------------------------------------------------------------------
/useStuck.js:
--------------------------------------------------------------------------------
1 | import useStateListener from "./useStateListener";
2 |
3 | export default function useStuck(ref, parent = ".wrapper") {
4 | return useStateListener(
5 | "scroll",
6 | () => `translate(0, ${ref.current.closest(parent).scrollTop}px)`
7 | ).state;
8 | };
9 |
--------------------------------------------------------------------------------
/useClickOutside.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import useEvent from "./useEvent";
3 |
4 | export default function useClickOutside(fn) {
5 | const ref = useRef(null);
6 | useEvent("click", e => ref.current.contains(e.target) || fn(), document);
7 | return ref;
8 | };
9 |
--------------------------------------------------------------------------------
/useImmer.js:
--------------------------------------------------------------------------------
1 | import { useReducer } from "react";
2 | import produce from "immer";
3 |
4 | export default function useImmutable(actions, initialState) {
5 | return useReducer(
6 | produce((state, { type, payload }) => actions[type](state)(payload)),
7 | initialState
8 | );
9 | };
10 |
--------------------------------------------------------------------------------
/useEvent.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 |
3 | export default function useEvent(event, handler, source = window) {
4 | useEffect(() => {
5 | source.addEventListener(event, handler);
6 | return () => {
7 | source.removeEventListener(event, handler);
8 | };
9 | }, [handler]);
10 | };
11 |
--------------------------------------------------------------------------------
/useInterval.js:
--------------------------------------------------------------------------------
1 | import { useRef, useEffect } from "react";
2 |
3 | export default function useInterval(cb, time = 1000) {
4 | const ref = useRef();
5 | useEffect(() => {
6 | ref.current = cb;
7 | const id = setInterval(() => ref.current(), time);
8 | return () => clearInterval(id);
9 | }, [time]);
10 | };
11 |
--------------------------------------------------------------------------------
/useDelay.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 |
3 | export default function useDelay(interval = 1000) {
4 | const [done, setDone] = useState(false);
5 | useEffect(() => {
6 | let id = setTimeout(() => { setDone(true); }, interval);
7 | return () => clearTimeout(id);
8 | }, [done]);
9 | return done;
10 | };
11 |
--------------------------------------------------------------------------------
/useFirebase.js:
--------------------------------------------------------------------------------
1 | import useStateListener from "./useStateListener";
2 |
3 | export default function useFirebase(project, path = "/") {
4 | const url = `https://${project}.firebaseio.com/${path}.json`;
5 | const source = new EventSource(url);
6 | return useStateListener("put", ({ data }) => JSON.parse(data).data, source)
7 | .state;
8 | }
9 |
--------------------------------------------------------------------------------
/useRouter.js:
--------------------------------------------------------------------------------
1 | import useStateListener from "./useStateListener";
2 |
3 | export default function useRouter() {
4 | const { state, setter } = useStateListener("popstate", () => location.pathname);
5 | const push = path => {
6 | history.pushState(null, "", path);
7 | setter(path);
8 | };
9 | return [state, push];
10 | };
11 |
--------------------------------------------------------------------------------
/useIsOnline.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import useEvent from "./useEvent";
3 |
4 | export default function useOnlineStatus() {
5 | const [status, setStatus] = useState(navigator && navigator.onLine);
6 | useEvent("online", () => setStatus(true));
7 | useEvent("offline", () => setStatus(false));
8 | return status;
9 | };
10 |
--------------------------------------------------------------------------------
/useScrollDetection.js:
--------------------------------------------------------------------------------
1 | import useStateListener from "./useStateListener";
2 |
3 | export default function useScrollDetection(refs, defaultSection) {
4 | return useStateListener("scroll", () =>
5 | refs.find(
6 | ref =>
7 | ref && ref instanceof Element && ref.getBoundingClientRect().top < 0
8 | )
9 | ).state;
10 | }
11 |
--------------------------------------------------------------------------------
/usePromise.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | export default function usePromise(func) {
4 | const [state, setState] = useState({});
5 | const handler = (...args) => {
6 | setState({ pending: true });
7 | func(...args)
8 | .then(res => setState({ res }))
9 | .catch(error => setData({ error }));
10 | };
11 | return [state, handler];
12 | };
13 |
--------------------------------------------------------------------------------
/useFetch.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 |
3 | export default function useFetch(url) {
4 | const [data, setData] = useState({ loading: true });
5 | useEffect(() => {
6 | let active = true;
7 | fetch(url)
8 | .then(response => response.json())
9 | .then(data => active && setData({ data }))
10 | .catch(error => setData({ error }));
11 | return () => (active = false);
12 | }, [url]);
13 | return data;
14 | }
15 |
--------------------------------------------------------------------------------
/useGeolocation.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 |
3 | export default function useGeolocation() {
4 | const [location, setLocation] = useState({});
5 | const handler = ({coords}) => setLocation(coords);
6 | useEffect(() => {
7 | navigator.geolocation.getCurrentPosition(handler);
8 | const id = navigator.geolocation.watchPosition(handler);
9 | return () => navigator.geolocation.clearWatch(id);
10 | }, []);
11 | return location;
12 | };
13 |
--------------------------------------------------------------------------------
/useBroadcast.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import useStateListener from "./useStateListener";
3 |
4 | export default function useBroadcast(channelName = "default") {
5 | const channel = useRef(new BroadcastChannel(channelName));
6 | const postMessage = msg => channel.current.postMessage(JSON.stringify(msg));
7 | const { state } = useStateListener(
8 | "message",
9 | e => JSON.parse(e.data),
10 | channel.current
11 | );
12 | return [state, postMessage];
13 | }
14 |
--------------------------------------------------------------------------------
/useStateListener.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 |
3 | export default function useStateListener(event, callback, source = window) {
4 | const [state, setState] = useState(callback());
5 | useEffect(() => {
6 | const handler = e => setState(callback(e, state));
7 | source.addEventListener(event, handler);
8 | return () => {
9 | source.removeEventListener(event, handler);
10 | };
11 | }, [callback, event, source]);
12 | return { state, setter };
13 | }
14 |
--------------------------------------------------------------------------------
/useStorage.js:
--------------------------------------------------------------------------------
1 | export default function useStorage(key, initialValue) {
2 | const [localValue, setLocalValue] = useState(
3 | () => JSON.parse(localStorage.getItem(key)) || initialValue
4 | );
5 |
6 | const setValue = value => {
7 | setLocalValue(value);
8 | localStorage.setItem(key, JSON.stringify(value));
9 | };
10 |
11 | useEvent("storage", e => {
12 | if (e.key === key) {
13 | setLocalValue(JSON.parse(localStorage.getItem(key)));
14 | }
15 | });
16 |
17 | return [localValue, setValue];
18 | };
19 |
--------------------------------------------------------------------------------
/useData.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Vue.js/Immmer objects with automatic change detection
3 | *
4 | * Notice: unlike Vue and Immer, the change detection is not recursive!
5 | *
6 | * Usage:
7 | *
8 | * const data = useData({ count: 0 });
9 | *
10 | * return ;
11 | *
12 | */
13 |
14 | export default function useData(data) {
15 | const [state, setState] = useState(data);
16 | return new Proxy(state, {
17 | set: (obj, prop, value) => setState({ ...state, [prop]: value})
18 | });
19 | }
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/useForm.js:
--------------------------------------------------------------------------------
1 | /* Usage:
2 | *
3 | * const [values, { radio, checkbox, tel }] = useForm({accept: "y", phone: "123"});
4 | *
5 | * return (
6 | *
12 | * );
13 | */
14 | export default function useForm(value) {
15 | const [values, setValues] = useState(value);
16 | const [touched, setTouchedState] = useState({});
17 | const [validity, setValidityState] = useState({});
18 |
19 | const generate = type => (name, ownValue) => {
20 | const isCheck = type === "checkbox";
21 | const isRadio = type === "radio";
22 | return {
23 | name,
24 | type,
25 | get checked() {
26 | return isRadio ? ownValue == values[name] : values[name];
27 | },
28 | get value() {
29 | return isRadio ? ownValue : values[name];
30 | },
31 | onChange({ target: { value, checked } }) {
32 | console.log(value, checked)
33 | setValues({ ...values, [name]: isCheck ? checked : value });
34 | },
35 | onBlur(e) {
36 | setTouchedState({ ...touched, [name]: true });
37 | setValidityState({ ...validity, [name]: e.target.validity.valid });
38 | }
39 | };
40 | };
41 | const factory = {
42 | input: generate("input"),
43 | tel: generate("tel"),
44 | checkbox: generate("checkbox"),
45 | radio: generate("radio")
46 | };
47 |
48 | return [{ values, validity, touched }, factory];
49 | }
50 |
--------------------------------------------------------------------------------