All are adopted from 8 Awesome React Hooks by Simon Holdorf
Found this link as well and have added some here ReactJS Hooks
All are adopted from 8 Awesome React Hooks by Simon Holdorf
Found this link as well and have added some here ReactJS Hooks
| // Adapted from https://betterprogramming.pub/how-to-use-media-queries-programmatically-in-react-4d6562c3bc97 | |
| // -----8<---- | |
| // BreakpointProvider | |
| // -----8<---- | |
| import React, { | |
| useState, | |
| useEffect, | |
| createContext, | |
| useContext} from 'react'; | |
| const defaultValue = {} | |
| const BreakpointContext = createContext(defaultValue); | |
| const BreakpointProvider = ({children, queries}) => { | |
| const [queryMatch, setQueryMatch] = useState({}); | |
| useEffect(() => { | |
| const mediaQueryLists = {}; | |
| const keys = Object.keys(queries); | |
| let isAttached = false; | |
| const handleQueryListener = () => { | |
| const updatedMatches = keys.reduce((acc, media) => { | |
| acc[media] = !!(mediaQueryLists[media] && mediaQueryLists[media].matches); | |
| return acc; | |
| }, {}) | |
| setQueryMatch(updatedMatches) | |
| } | |
| if (window && window.matchMedia) { | |
| const matches = {}; | |
| keys.forEach(media => { | |
| if (typeof queries[media] === 'string') { | |
| mediaQueryLists[media] = window.matchMedia(queries[media]); | |
| matches[media] = mediaQueryLists[media].matches | |
| } else { | |
| matches[media] = false | |
| } | |
| }); | |
| setQueryMatch(matches); | |
| isAttached = true; | |
| keys.forEach(media => { | |
| if(typeof queries[media] === 'string') { | |
| mediaQueryLists[media].addListener(handleQueryListener) | |
| } | |
| }); | |
| } | |
| return () => { | |
| if(isAttached) { | |
| keys.forEach(media => { | |
| if(typeof queries[media] === 'string') { | |
| mediaQueryLists[media].removeListener(handleQueryListener) | |
| } | |
| }); | |
| } | |
| } | |
| }, [queries]); | |
| return ( | |
| <BreakpointContext.Provider value={queryMatch}> | |
| {children} | |
| </BreakpointContext.Provider> | |
| ) | |
| } | |
| function useBreakpoint() { | |
| const context = useContext(BreakpointContext); | |
| if(context === defaultValue) { | |
| throw new Error('useBreakpoint must be used within BreakpointProvider'); | |
| } | |
| return context; | |
| } | |
| export {useBreakpoint, BreakpointProvider}; | |
| // -----8<---- | |
| // Using BreakpointProvider | |
| // -----8<---- | |
| import React from 'react'; | |
| import ReactDOM from 'react-dom'; | |
| import App from './App'; | |
| import {BreakpointProvider} from './breakpoint' | |
| const queries = { | |
| xs: '(max-width: 320px)', | |
| sm: '(max-width: 720px)', | |
| md: '(max-width: 1024px)', | |
| or: '(orientation: portrait)', // we can check orientation also | |
| } | |
| ReactDOM.render( | |
| <BreakpointProvider queries={queries}> | |
| <App /> | |
| </BreakpointProvider>, document.getElementById('root')); | |
| // -----8<---- | |
| // Using useBreakpoint | |
| // -----8<---- | |
| import React from 'react' | |
| import {useBreakpoint} from './breakpoint.js' | |
| const Usage = () => { | |
| const breakpoints = useBreakpoint(); | |
| const matchingList = Object.keys(breakpoints).map(media => ( | |
| <li key={media}>{media} ---- {breakpoints[media] ? 'Yes' : 'No'}</li> | |
| )) | |
| return ( | |
| <ol> | |
| {matchingList} | |
| </ol> | |
| ) | |
| } |
| import React, {useEffect, useState} from 'react'; | |
| const useFade = (initial) => { | |
| const [show, setShow] = useState(initial); | |
| const [isVisible, setVisible] = useState(show); | |
| // Update visibility when show changes | |
| useEffect(() => { | |
| if (show) setVisible(true); | |
| }, [show]); | |
| // When the animation finishes, set visibility to false | |
| const onAnimationEnd = () => { | |
| if (!show) setVisible(false); | |
| }; | |
| const style = { animation: `${show ? "fadeIn" : "fadeOut"} 3s` }; | |
| // These props go on the fading DOM element | |
| const fadeProps = { | |
| style, | |
| onAnimationEnd | |
| }; | |
| return [isVisible, setShow, fadeProps]; | |
| }; | |
| function App() { | |
| const [isVisible, setVisible, fadeProps] = useFade(false); | |
| // some call that will launch setVisible(!isVisible) | |
| const res = useFetch('https://someapi/api/hello', options, ()=>setVisible(!isVisible)); | |
| return ( | |
| <div className="App"> | |
| <h1 className="App-header"> | |
| React | |
| </h1> | |
| {isVisible && <h2 {...fadeProps}>{res.response}</h2>} | |
| </div> | |
| ) | |
| } | |
| // --------- | |
| // CSS | |
| // --------- | |
| // | |
| // @keyframes fadeIn { | |
| // 0% { opacity: 0; } | |
| // 100% { opacity: 1; } | |
| // } | |
| // | |
| // @keyframes fadeOut { | |
| // 0% { opacity: 1; } | |
| // 100% { opacity: 0; } | |
| // } |
| const Input = styled.input` | |
| padding: 0.5em; | |
| margin: 0.5em; | |
| color: #BF4F74; | |
| background: papayawhip; | |
| border: none; | |
| border-radius: 3px; | |
| `; | |
| class Form extends React.Component { | |
| constructor(props) { | |
| super(props); | |
| this.inputRef = React.createRef(); | |
| } | |
| render() { | |
| return ( | |
| <Input | |
| ref={this.inputRef} | |
| placeholder="Hover to focus!" | |
| onMouseEnter={() => { | |
| this.inputRef.current.focus() | |
| }} | |
| /> | |
| ); | |
| } | |
| } | |
| render( | |
| <Form /> | |
| ); |
| // | |
| // You can not set state after the component is dismounted ( you will get a Warning in console ) | |
| // EG: | |
| // Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it | |
| // indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous | |
| // tasks in a useEffect cleanup function. | |
| // | |
| // This method, attaches a boolean to the mounted reference, which can be tested for truthy. | |
| const mounted = userRef(true); | |
| useEffect( () => { | |
| sleep(1000).then( () => { | |
| if (mounted.current) { | |
| setState(someValue); | |
| } | |
| }) | |
| return () => { | |
| mounted.current = false; | |
| }; | |
| }, [] ); |
| import React, {useEffect, useRef} from 'react'; | |
| const useComponentDidMount = (onMountHandler) => { | |
| useEffect(()=>{ | |
| onMountHandler(); | |
| },[]); | |
| } | |
| const TalkBubble = ({children}) => { | |
| const divRef = useRef(); | |
| useComponentDidMount(()=>{ | |
| divRef.current.scrollIntoView({ behavior: "smooth" }); | |
| }) | |
| return ( | |
| <div ref={divRef}> | |
| {children} | |
| </div> | |
| ) | |
| } | |
| export default TalkBubble |
| import React, { useState } from 'react'; | |
| function Example() { | |
| const [count, setCount] = useState(0); | |
| return ( | |
| <div> | |
| <p>You clicked {count} times</p> | |
| <button onClick={() => setCount(count + 1)}> | |
| Click me | |
| </button> | |
| </div> | |
| ); | |
| } |
| import { useState, useEffect, useRef, useCallback } from "react"; | |
| /** | |
| * adopted from https://blog.logrocket.com/using-react-hooks-to-create-sticky-headers/ | |
| **/ | |
| const StickyHeader = (defaultSticky = false) => { | |
| const [isSticky, setIsSticky] = useState(defaultSticky); | |
| const tableRef = useRef(null); | |
| const toggleSticky = useCallback( | |
| ({ top, bottom }) => { | |
| if (top <= 0 && bottom > 2 * 68) { | |
| !isSticky && setIsSticky(true); | |
| } else { | |
| isSticky && setIsSticky(false); | |
| } | |
| }, | |
| [isSticky] | |
| ); | |
| useEffect(() => { | |
| const handleScroll = () => { | |
| toggleSticky(tableRef.current.getBoundingClientRect()); | |
| }; | |
| window.addEventListener("scroll", handleScroll); | |
| return () => { | |
| window.removeEventListener("scroll", handleScroll); | |
| }; | |
| }, [toggleSticky]); | |
| return { tableRef, isSticky }; | |
| }; | |
| export default StickyHeader; |
| /** | |
| Create a custom hook with clicking inside of wrapped components. | |
| First, create a custom hook that takes in a ref and a callback to handle the click event. | |
| Then we make use of useEffect to append and clean up the click event. | |
| Finally, we use useRef to create a ref for the component to be clicked and pass it to the useClickInside hook. | |
| **/ | |
| import React, { useRef, useEffect } from 'react'; | |
| import * as ReactDom from "react-dom"; | |
| const useClickInside = (ref, callback) => { | |
| const handleClick = e => { | |
| if ( ref.current && ref.current.contains(e.target)) { | |
| callback(); | |
| } | |
| }; | |
| useEffect(()=>{ | |
| // document.addEventListener('click',handleClick); <<--- this is on the whole document, potentiall wrong. | |
| ref.current.addEventListener('click',handleClick); | |
| return () => { | |
| // document.removeEventListener('click', handleClick);<<--- this is on the whole document, potentiall wrong. | |
| ref.current.removeEventListener('click',handleClick); | |
| } | |
| }) | |
| } | |
| const HitBox = ({ onClickInside }) => { | |
| const clickRef = useRef(); | |
| useClickInside(clickRef, onClickInside); | |
| return (<div | |
| className="hit-box" | |
| ref={clickRef} | |
| style={{ | |
| border: '5px solid green', | |
| height: 300, | |
| width: 600, | |
| display: 'flex', | |
| justifyContent: 'center', | |
| alignItems: 'center' | |
| }} | |
| > | |
| <p>Hit the box!</p> | |
| </div>); | |
| } | |
| ReactDom.render(<HitBox onClickInside={()=>alert('hit the box')}/>,document.getElementById('root')); |
| import React, { useRef, useEffect } from 'react'; | |
| import * as ReactDom from "react-dom"; | |
| const useClickOutside = (ref, callback) => { | |
| const handleClick = e => { | |
| if ( ref.current && !ref.current.contains(e.target)) { | |
| callback(); | |
| } | |
| }; | |
| useEffect(()=>{ | |
| // document.addEventListener('click',handleClick); <<--- this is on the whole document, potentiall wrong. | |
| ref.current.addEventListener('click',handleClick); | |
| return () => { | |
| // document.removeEventListener('click', handleClick);<<--- this is on the whole document, potentiall wrong. | |
| ref.current.removeEventListener('click',handleClick); | |
| } | |
| }) | |
| } | |
| const HitBox = ({ onClickOutside }) => { | |
| const clickRef = useRef(); | |
| useClickOutside(clickRef, onClickOutside); | |
| return (<div | |
| className="hit-box" | |
| ref={clickRef} | |
| style={{ | |
| border: '5px dashed green', | |
| height: 300, | |
| width: 600, | |
| display: 'flex', | |
| justifyContent: 'center', | |
| alignItems: 'center' | |
| }} | |
| > | |
| <p>Don't hit the box!</p> | |
| </div>); | |
| } | |
| ReactDom.render(<HitBox onClickOutside={()=>alert('dont hit the box')}/>,document.getElementById('root')); |
| /** | |
| * Component did mount is part of the State and Lifecycle, https://reactjs.org/docs/react-component.html#componentdidmount | |
| * This is just another way of doing it. | |
| * For the second argument, we simply use useEffect with an empty array, | |
| * to execute the provided callback once as soon as the component is mounted. | |
| */ | |
| import React, {useEffect, useRef, useState} from 'react'; | |
| import * as ReactDom from "react-dom"; | |
| const useComponentDidMount = onMountHandler => { | |
| useEffect(()=>{ | |
| onMountHandler(); | |
| },[]); | |
| } | |
| const MountComponent = props => { | |
| useComponentDidMount(()=>{ | |
| console.log('this component has loaded') | |
| }) | |
| return <p>check the console</p> | |
| } | |
| ReactDom.render(<MountComponent/>, document.getElementById('root')); |
| /** | |
| * Component will un mount is part of the State and Lifecycle, | |
| * https://reactjs.org/docs/react-component.html#componentdidmount | |
| * This is just another way of doing it. | |
| * For the second argument, we simply use useEffect with an empty array, | |
| * to execute the provided callback once as soon as the component is mounted. | |
| */ | |
| import React, {useEffect, useRef, useState} from 'react'; | |
| import * as ReactDom from "react-dom"; | |
| const useComponentWillUnmount = onUnmountHandler => { | |
| useEffect(()=>()=>{ | |
| onUnmountHandler(); | |
| },[]); | |
| } | |
| const UnmountComponent = props => { | |
| useComponentWillUnmount(()=>{ | |
| console.log('this component has un mounted') | |
| }) | |
| return <p>check the console</p> | |
| } | |
| ReactDom.render(<UnmountComponent/>, document.getElementById('root')); |
| /** | |
| The useContext accepts the value provided by React.createContext | |
| and then re-render the component whenever its value changes but | |
| you can still optimize its performance by using memoization. | |
| **/ | |
| // -----8<---- AuthContext.js -----8<---- | |
| import React from 'react'; | |
| const authContext = React.createContext({ | |
| auth: null, | |
| login: () => {}, | |
| logout: () => {}, | |
| }); | |
| export default authContext; | |
| // -----8<---- Login.js -----8<---- | |
| import React, { useContext } from 'react'; | |
| import AuthContext from './AuthContext'; | |
| const Login = () => { | |
| const auth = useContext(AuthContext); | |
| return ( | |
| <> | |
| <button onClick={auth.login}>Login</button> | |
| </> | |
| ); | |
| }; | |
| export default Login; | |
| // -----8<---- Logout.js -----8<---- | |
| import React, { useContext } from 'react'; | |
| import AuthContext from './AuthContext'; | |
| const Logout = () => { | |
| const auth = useContext(AuthContext); | |
| return ( | |
| <> | |
| <button onClick={auth.logout}>Click To Logout</button> | |
| </> | |
| ); | |
| }; | |
| export default Logout; | |
| // -----8<---- App.jsx -----8<---- | |
| import React, { useState } from 'react'; | |
| import LogIn from './Login'; | |
| import LogOut from './Logout'; | |
| import AuthContext from './AuthContext'; | |
| const App = () => { | |
| const [auth, setAuth] = useState(false); | |
| const login = () => { | |
| setAuth(true); | |
| }; | |
| const logout = () => { | |
| setAuth(false); | |
| }; | |
| return ( | |
| <React.Fragment> | |
| <AuthContext.Provider | |
| value={{ auth: auth, login: login, logout: logout }} | |
| > | |
| <p>{auth ? 'Hi! You are Logged In' : 'Oope! Kindly Login'}</p> | |
| <LogIn /> | |
| <LogOut /> | |
| </AuthContext.Provider> | |
| </React.Fragment> | |
| ); | |
| }; | |
| export default App; |
| /** | |
| * The useFetch hook can be used to implement fetch in a declarative way. | |
| * | |
| * First, use useState to initialize the response and error state variables. | |
| * Then we use useEffect to asynchronously call fetch and update the state. | |
| * Finally, we return an object that contains the response/error variables. | |
| */ | |
| import React, {useEffect, useState} from 'react'; | |
| import * as ReactDom from "react-dom"; | |
| const useFetch = (url, options) => { | |
| const [response, setResponse] = useState(null); | |
| const [error, setError] = useState(null); | |
| useEffect(() => { | |
| const fetchData = async () => { | |
| try { | |
| const res = await fetch(url, options); | |
| const json = await res.json(); | |
| setResponse(json); | |
| } catch (error) { | |
| setError(error); | |
| } | |
| }; | |
| // @see https://medium.com/javascript-in-plain-english/how-to-use-async-function-in-react-hook-useeffect-typescript-js-6204a788a435 | |
| (async () => { | |
| await fetchData(); | |
| })(); | |
| }, []); | |
| return {response, error}; | |
| } | |
| const FetchPerson = props => { | |
| const res = useFetch('https://swapi.co/api/people/1', {}); | |
| if (!res.response) { | |
| return <div>Loading...</div> | |
| } | |
| const person = res.response.name; | |
| return (<div><span>{person}</span></div>) | |
| } | |
| ReactDom.render(<FetchPerson/>, document.getElementById('root')); |
| /** | |
| * Create a custom hook with that executes a interval function. | |
| * | |
| * First, create a custom hook taking in a callback and a delay. | |
| * Then use useRef to create a ref for the callback. | |
| * Finally, useEffect to remember the latest callback and to set up the interval and clean up. | |
| */ | |
| import React, {useEffect, useRef, useState} from 'react'; | |
| import * as ReactDom from "react-dom"; | |
| const useInterval = (callback, delay) => { | |
| const savedCallback = useRef(); | |
| useEffect(() => { | |
| savedCallback.current = callback; | |
| }, [callback]); | |
| useEffect(() => { | |
| const tick = function tick() { | |
| savedCallback.current(); | |
| } | |
| if (delay !== null) { | |
| let id = setInterval(tick, delay); | |
| return () => clearInterval(id); | |
| } | |
| }, [delay]) | |
| } | |
| const ResourceCounter = props => { | |
| const [resources, setResources] = useState(0); | |
| useInterval(() => { | |
| setResources(resources + 2) | |
| }, 2500) | |
| return <p>{resources}</p> | |
| } | |
| ReactDom.render(<ResourceCounter/>, document.getElementById('root')); |
| import { useMemo, useCallback } from "react"; | |
| // refer to https://reactjs.org/docs/hooks-reference.html#usememo | |
| function MemoNizeValue() { | |
| const [count,setCount] = useState(60); | |
| const expensiveCount = useMemo(()=>{ | |
| return count **2; | |
| }, [count]); // Only execute, when the count changes. Recompute. | |
| return <></> | |
| } | |
| function MemoNizeFunction() { | |
| const [count,setCount] = useState(60); | |
| const showCount = useCallback(()=>{ | |
| // some expensive function call. | |
| },[count]); // <<--- reuse the same function object | |
| return <><SomeChild handler={showCount}/></> | |
| } |
| /** | |
| First, we create a custom hook that takes in a value. | |
| Then we use the useRef hook to create a ref for the value. | |
| Finally, we use useEffect to remember the latest value. | |
| **/ | |
| import React, { useState, Component } from 'react'; | |
| import * as ReactDom from "react-dom"; | |
| const usePrevious = value => { | |
| const ref = React.useRef(); | |
| React.useEffect(()=>{ | |
| ref.current = value; | |
| }); | |
| return ref.current; | |
| } | |
| const MoneyCount = () => { | |
| const [value,setValue] = React.useState(0); | |
| const lastValue = usePrevious(value); | |
| return (<div> | |
| <p>Current: {value} - Previous: {lastValue}</p> | |
| <button onClick={()=>{setValue(value+1)}}>increment</button> | |
| </div>); | |
| } | |
| ReactDom.render(<MoneyCount/>,document.getElementById('root')); |
| /** | |
| Create a custom hook with a callback and a delay. | |
| Then we use the useRef hook to create a ref for the callback function. | |
| Finally, we make use of useEffect twice. | |
| One time for remembering the last callback and one time for setting up the timeout and cleaning up. | |
| **/ | |
| import React, { useState, Component } from 'react'; | |
| import * as ReactDom from "react-dom"; | |
| const useTimeout = (callBack, delay) => { | |
| const savedCallBack = React.useRef(); | |
| React.useEffect(()=>{ | |
| savedCallBack.current = callBack; | |
| },[callBack]); | |
| React.useEffect(()=>{ | |
| function tick() { | |
| savedCallBack.current(); | |
| } | |
| if ( delay !== null){ | |
| let id = setTimeout(tick,delay); | |
| return () => clearTimeout(id); | |
| } | |
| },[delay]); | |
| } | |
| const ExampleTimerFiveSeconds = props => { | |
| const [seconds,setSeconds] = React.useState(0); | |
| useTimeout(()=>{ | |
| setSeconds(seconds+1 ); | |
| },5000); | |
| return <p>{seconds}</p> | |
| } | |
| ReactDom.render(<ExampleTimerFiveSeconds/>,document.getElementById('root')); |
| /** | |
| Create a custom hook to get the current size of the browser window. | |
| This hook returns an object containing the window's width and height. | |
| If executed server-side (no window object) the value of width and height will be undefined. | |
| **/ | |
| import { useState, useEffect } from "react"; | |
| // Usage | |
| function App() { | |
| const size = useWindowSize(); | |
| return ( | |
| <div> | |
| {size.width}px / {size.height}px | |
| </div> | |
| ); | |
| } | |
| // Hook | |
| function useWindowSize() { | |
| // Initialize state with undefined width/height so server and client renders match | |
| // Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/ | |
| const [windowSize, setWindowSize] = useState({ | |
| width: undefined, | |
| height: undefined, | |
| }); | |
| useEffect(() => { | |
| // Handler to call on window resize | |
| function handleResize() { | |
| // Set window width/height to state | |
| setWindowSize({ | |
| width: window.innerWidth, | |
| height: window.innerHeight, | |
| }); | |
| } | |
| // Add event listener | |
| window.addEventListener("resize", handleResize); | |
| // Call handler right away so state gets updated with initial window size | |
| handleResize(); | |
| // Remove event listener on cleanup | |
| return () => window.removeEventListener("resize", handleResize); | |
| }, []); // Empty array ensures that effect is only run on mount | |
| return windowSize; | |
| } |