# React Hooks
TIP
- https://usehooks-ts.com/ (opens new window)
- https://github.com/enaqx/awesome-react#react-hooks (opens new window)
- https://github.com/rehooks/awesome-react-hooks (opens new window)
- Thinking in React Hooks (opens new window)
- usehooks.com - Easy to understand React Hook recipes (opens new window)
- usehooks.com (opens new window)
- useful-custom-react-hooks (opens new window)
- Hooks are just functions.
- Hooks don't work in Class.
- Optional
- No breaking changes
Why Hooks ?
Reusing Logic (Leads to wrapper hell)
- HOC
- Render Props
Huge Components
- Similar things in different places like subscribe/unsubscribe in
componentDidMount/componentWillUnMount
Classes are confusing
- Hooks allows us to use all react features without class
- Bad for minifying, hot reload, etc
Mixins are bad
State Management Libraries
- Makes component reuse difficult
# Rules of Hooks
- Linter will implement the rules -
eslint-plugin-react-hooks
- React depends on order of the Hook calls in a component
- Rules
- Don't call hooks inside - loop, conditions & nested functions.
- Call from - Function Component & Custom Hooks. Not from regular functions
# useState
- In class
this.setState({foo:3, bar:4})
- states are merged. But in hooks its replaced.
import React, { useState } from "react";
function Foo(props) {
// intialValue can be object, string, number, array etc
// returns a value & function to update value
const [value, setValue] = useState(initialValue);
const [state2, setState2] = useState(initialValue2); // can use multiple times
//createExpensiveObject() is only called once, Not called on subsequent render
const [value, setValue] = useState(() => createExpensiveObject(props.count));
// set next value
setValue(nextValue);
// set next value - using prev value
setValue((prev) => {
// calculate next value using prev
//
// return next value
// If object, we can also merge other values {...prevValues, nextValue}
});
// if next === prev then no re-render takes place
}
# useEffect
- Allows us to write imperative code which is not possible in react's component which is purely a functional code.
- React component does not allow sideeffects like mutations, Async, timers, etc directly
- Used for side-effects like Data fetching, subscription, DOM changes, etc
- Called AFTER every render & defers running till BrowserDOM is painted.
- Lifecycles combined together -
componentDidMount, componentDidUpdate & componentWillUnMount
- Lifecycles combined together -
- Most of the effects are Asynchronous. But for some synchronous effect like measure layout we have a
useLayoutEffect
- 2 Types
- Needs cleanUp (to avoid memory leak) - eg: subscription
- No cleanUp - eg: Logging, data fetching, DOM changes
useLayoutEffect()
- Not deferred. That is, it runs before BrowserDOM is painted.
- Use only if
useEffect()
cause a problem
function Foo(props) {
// we can use multiple effects. They maintain the same order as written.
useEffect(() => {
// This inner function is different everytime render is performed.
// Because in js each function is separate when created.
// ()=>{} === ()=>{} // false
// This Helps maintain the correct state on each render
// clean up function
// called after every render & unmount,
// called before running next useEffect()
// Eg: clear old eventhandlers we don't need multiple copies of event handlers
// Eg: unsubscribe()
return () => {
//...
};
// Dependencies
// - Run effect if atleast one dependency change
// - empty array - run effect Only once on mount. It mesns it's independent of any state, props.
// - setFoo - such functions won’t change on re-renders. It’s safe to omit.
// - "dispatch()" from useReducer - safe to omit.
}, [foo, bar]);
}
# useContext
- If context changes then react will re-render
import MyContext from "./path/MyContext";
function Foo(props) {
// value passed depends on <MyContext.Provider value="..."> ancestor
const myValue = useContext(MyContext);
}
# useReducer
- An alternative to
useState
if state is complex & state updates are adhoc - It's similar to Redux
const initialState = {
/*.....*/
};
// optional
// function initFunc(initialArgs) {
// return {
// /*.....*/
// };
// }
function Foo(props) {
const [state, dispatch] = useReducer(reducer, initialState);
// optional
// const [state, dispatch] = useReducer(reducer, initialArgs, initFunc);
}
// reducer is a function
// (preState, action) => nextState
function reducer(prevState, action) {
switch (action.type) {
case "FOO":
// calculate
return { ...prevState, newFoo };
case "BAR":
// calculate
return { ...prevState, newBar };
// optional
case "RESET":
return initFunc(action.payload);
default:
throw new Error("Invalid action type");
}
}
# useCallback/useMemo
- Used for Optimization - Cache & avoid heavy calculations on every render based on dependencies.
- Use rarely and carefully
// useCallback(func, deps) === useMemo(() => func, deps )
// returns callback
const memoizedCallback = useCallback(() => {
// This function is a memoization function
// It will cache the value until dependencies changes
}, [input]);
// returns value
const memoizedValue = useMemo(() => /*somethingHeavy(a, b);*/, [a, b]);
# useRef
- Used to store an object known as
ref
. Persist for lifetime of Component. - But it's
obj.current
can be mutated on render. - A change in
obj.current
won't trigger re-render - Can also be used to store previous state, props values. (save in
useEffect()
).
// create
// - initialValue is .current
const inputEl = useRef(initialValue) // initialValue can be null or some value.
// add
<input ref={inputEl} type="text" />
// use
inputEl.current.focus()
# Building your own hooks
- Name starts with
use
likeuseValidation()
function Foo(props) {
// call useMyCustomHook() with any arg value which can be state/prop too.
}
function useMyCustomHook(value) {
// use react builtin hooks
// can be state or effect or anything
//
// value is optional
//
// return any state values or anything else needed or just return nothing
}