Shubham Bansal
What is React Hooks
React hooks are a type of function that allows the users to hook into react state and life-cycle features from function components. React provides built-in hooks like useState, useEffect, useReducer, useRef, use Callback, useContext, useMemo and we can also create your own custom hooks.
React hooks are available from react version 16.8. Before the introduction of hooks, state can be maintained only in-class components, not in functional components. After the introduction of hooks, state can be maintained in functional components too.
The idea of using hooks makes it possible to create full-fledged functional components, while using all the React features. This allows us to make everything easier, unlike classes.
The main problems which were solved by release of hooks in React 16.8:
- Wrapper ****Hell
- Classes
- Side effect
In ReactJS, the most difficult thing is to reuse logic in stateful components. Before the appearance of React hooks, there was no way to reuse the logic of behavior to the component, for example, to connect it to the store.
The following examples make it clear why hooks are useful.
Using Classes :
import React, { Component } from ‘react’;
class App extends Component {
constructor(props) {
super(props);
this.state = {
isButtonClicked: false,
}
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState((prevState) => ({
isButtonClicked: !prevState.isButtonClicked,
}));
}
Using Hooks :
import React, { useState } from ‘react’;
const App = () => {
const [isButtonClicked, setIsButtonClickedStatus] = useState(false);
return (
<button
onClick={() => setIsButtonClickedStatus(!isButtonClicked)}
>
{isButtonClicked ? ‘Clicked’ : ‘Click me, please’}
</button>
);
};
From this, we can understand that hooks work similarly to such concepts as a state.
Rules of Using React Hooks
Hooks are just JavaScript functions, but they require two specific rules:
- Hooks should be performed at the very top of the function hierarchy which means that the users should not call hooks in conditions and loops, otherwise the reaction cannot guarantee the execution order of hooks.
- We should call hooks only in React functions or functional components or call hooks from custom hooks.
What is Custom Hooks
Custom Hook is a JavaScript function which we create by ourselves, when we want to share logic between other JavaScript functions. It allows you to reuse some piece of code in several parts of your app.
Advantages and disadvantages
The “useContext” hook has been a blessing for greatly improving the readability of JSX as it allows context values to be read outside of JSX. This was also previously possible in class components by using the static “contextType” property but is even cleaner with “useContext”.
Aside from the code being easier to read it is also much easier to read the component tree in the React dev tools when debugging. The value of this really adds up for components which previously used multiple nested contexts.
With class components the setup and teardown of side effects were split across multiple lifecycle methods like the event listeners could be added in “componentDidMount” and later removed in “componentWillUnmount”. If any component has multiple side effects this could lead to less readable code with a related logic split across several incohesive lifecycle methods.
But the “use Effect” solves this problem by handling both the setup and teardown of side effects. It does so by allowing the effect function to return a function to teardown the effect. It is evident from the below example:
useEffect(() => {
window.addEventListener(“resize”, resizeHandler);
return () =>
window.removeEventListener(“resize”, resizeHandler);
}, []);
Custom hooks are considerably a great mechanism for sharing logic across various components. A custom hook is simply a function which uses one or more React hooks and it can be called within a functional component, just like a standard hook.
A nice example is a custom hook for tracking the result of a media query and returning this state to the component. It demonstrates how hooks can be combined. It uses “useEffect” to set up an event listener to track changes in whether a media query is matched or not. It also uses “useState” to store this state and return the result to the calling component.
function use MatchMedia(mediaQuery) {
const [matches, setMatches] = useState(window.matchMedia(mediaQuery).matches);
useEffect(() => {
const updateMatches = event => {
setMatches(event.matches);
};
const mediaQueryList = window.matchMedia(mediaQuery);
setMatches(mediaQueryList.matches);
mediaQueryList.addListener(updateMatches);
return () => mediaQueryList.removeListener(updateMatches);
}, [mediaQuery]);
return matches;
}
- hooks are easier to work with and to test (as separated functions from React components*) and make the code look cleaner, easier to read — a related logic can be tightly coupled in a custom hook. Check below visualization of differences between standard stateful component and a one using hooks:
- By separated functions I mean writing a logic as your own hooks that are just functions that return values or functions used for accessing and manipulating state with use of standard React hooks. We still have to test them through a mock React component but I think it’s more readable and easier than testing HOCs:
import React, { useState } from ‘react’;
export const useTextField = name => {
const [value, setValue] = useState(”);
const onChange = event => {
setValue(event.target.value);
};
return {
name,
value,
onChange,
placeholder: name,
};
};
const InputDemoWithHooks = () => {
const nameField = useTextField(‘name’);
return <input type=”text” {…nameField} />;
};
export default InputDemoWithHooks;
And tests for the hook:
import React from ‘react’;
import { mount } from ‘enzyme’;
const TestHook = ({ callback }) => {
callback();
return null;
};
export const testHook = (callback) => {
mount(<TestHook callback={callback} />);
};
// tests
import { act } from ‘react-dom/test-utils’;
import { testHook } from ‘./testUtils’;
import { useTextField } from ‘../InputDemoWithHooks’;
let nameField;
beforeEach(() => {
testHook(() => {
nameField = useTextField(‘name’);
});
});
describe(‘useTextField’, () => {
test(‘should have an onChange function’, () => {
expect(nameField.onChange).toBeInstanceOf(Function);
});
test(‘should have correct name’, () => {
expect(nameField.name).toBe(‘name’);
});
test(‘should update the value when onChange is called’, () => {
act(() => {
nameField.onChange({ target: { value: ‘nitin’ } });
});
expect(nameField.value).toBe(‘nitin’);
});
});
- code that uses hooks is more readable and have less LOC (lines of code), let’s look on a standard class component example:
// example class component that we will rewrite as function component
class Form extends React.Component {
constructor(props) {
super(props);
this.saveToDraft = debounce(500, this.saveToDraft);
};
state = {
// Fields values
fields: {},
// Draft saving meta
draft: {
isSaving: false,
lastSaved: null,
},
};
saveToDraft = (data) => {
if (this.state.isSaving) {
return;
}
this.setState({
isSaving: true,
});
makeSomeAPICall().then(() => {
this.setState({
isSaving: false,
lastSaved: new Date(),
})
});
}
componentDidUpdate(prevProps, prevState) {
if (!shallowEqual(prevState.fields, this.state.fields)) {
this.saveToDraft(this.state.fields);
}
}
render() {
return (
<form>
{/* Draft saving meta render */}
{/* Inputs render */}
</form>
);
};
}
And the same component as above rewritten as function component
// the above class component rewritten as function component,
const Form = () => {
// Our state
const [fields, setFields] = useState({});
const [draftIsSaving, setDraftIsSaving] = useState(false);
const [draftLastSaved, setDraftLastSaved] = useState(false);
useEffect(() => {
const id = setTimeout(() => {
if (draftIsSaving) {
return;
}
setDraftIsSaving(true);
makeSomeAPICall().then(() => {
setDraftIsSaving(false);
setDraftLastSaved(new Date());
});
}, 500);
return () => clearTimeout(id);
}, [fields]);
return (
<form>
{/* Draft saving meta render */}
{/* Inputs render */}
</form>
);
}
- thanks to hooks it’s easy to make code more reusable / composable (also they don’t create another element in DOM like HOCs do) — with HOCs we are separating unrelated state logic into different functions and injecting them into the main component as props, although with Hooks, we can solve this just like HOCs but without a wrapper hell. Let’s compare below example:
example reusable logic for liking posts on a feed implemented as HOC,
And the same logic rewritten with use of hooks:
componentDidMount rewritten for React Hooks,
- hooks are going to work better with future React optimizations (like ahead of time compilation and components folding) — components folding might be possible in future — what means dead code elimination at compile time (less JS code to download, less to execute)
- Even now, minification of functions in JavaScript is much better than minification of JavaScript classes. It’s not possible for example to minify method names of JS classes as every method can be either private or public, so we don’t know when it’s used outside the class. Less code to download, process and execute has a positive impact for the end user. Check comparison:
comparison between minification of class component and function component that uses hooks.
- Hooks show the real nature of React which is functional, using classes make developers easier to make mistakes and use React anti-patterns. In my career I encountered examples where people with object-oriented programming backgrounds tended to extend class components… that were hard times for debugging
- hooks are very convenient to re-use stateful logic, this is one of their selling points. But this is not applicable when an app is built using some state management library and stateful logic doesn’t live in React components. So for an app on which I work in its current state hooks are mostly for readability and to make it future-proof because for local logic we use MobX stores
React Hooks vs older approaches for reusable logic
- Read code examples and think of differences in implementing logic between in a Class Component:
- and as Mixins (deprecated):
- and as HOCs (Higher Order Components):
- and as Render Props:
- and as Hooks:
Mixins are deprecated API. HOCs disadvantage is they create additional DOM elements so when you use few HOCs, then you will see your component nested in few DOM elements. Render props if nested are creating similar structures as “callback hell” in the past. Hooks have no disadvantages of using multiple custom hooks on a single component.
When and How to Use
When we want to share the logic between other components, we can extract it to a separate function. According to official documents, the custom hook has to:
- start with the key word use
- call other hooks
Because custom hook is a JS function, the Rules of Hooks apply to it as well. Those are:
- Never call Hooks from inside a loop, condition or nested function
- Hooks should sit at the top-level of your component
- Only call Hooks from React functional components
- Never call a Hook from a regular function
- Hooks can call other Hooks
Disadvantages
- It’s hard to reuse stateful logic between components.
- Complex components become hard to understand.
Limitations
- Only call hooks at the top Level.
- Only Call hooks from React function components.
Name:- npm i stack-ui-hooks
- useArray
- useSet
- useMap
- useList
- useBoolean
- useNumber
- useCheckbox
- useActive
- useAsync
- useBind
- useDidMount
- useDidUpdate
- useClickOutside
- useDelay
- useDocumentReady
- useFocus
- useGoogleAnalytics
- useImage
- useInput
- useLogger
- useOnclick
- usePageLoad
- usePersist
- usePrevious
- usePersist
- useScript
- useWindowSize
- UseActive
- UseAsync
- UseBind
- UseCheckbox
- UseCounter
- useDidMount
- useDidUpdate
- useEqualEffect
- useFetch
- useField
- useFocus
- useGlobalState
- useHover
- useMergeState
- useNestedBind
- useNestedState
- useStateCallback
- useToggle
- useTouch
- useUndo
- useWillUnmount
- useCounter
- useFocus
- useInput
- useInterval
- useLifecycleHooks
- useMergeState
- useOnMount
- useOnUnmount
- usePrevius
- useTimeout
- useAsync
- useAsyncFn
- useAsyncRetry
- useAudio
- useBatery
- useBeforeUnload
- useClickAway
- useCookie
- useCopyToClipboard
- useCounter
- useCss
- useCustomCompareEffect
- useDebounce
- useDeepCompareEffect
- useDefault
- useDrop
- useDropArea
- useEffectOnce
- useEnsuredForwardedRef
- useError
- useEvent
- useFavicon
- useFirstMountState
- useFullscreen
- useGeolocation
- useGetSet
- useGetSetState
- useHarmonicInterval
- useHash
- useHoverDirty
- useIdle
- useIntersection
- useInterval
- useIsomorhicLayoutEffect
- useKey
- useKeyboardJs
- useLatest
- useLifecycle
- useLocalStorage
- useLocation
- useLockBodyScroll
- useLogger
- useLongPress
- useMeasure
- useMeasureDirty
- useMedia
- useMediaDevices
- useMediatedState
- useMethod
- useMotion
- useMount
- useMountedState
- useMouse
- useMouseWheel
- useMultiStateValidator
- useNetworkState
- useObservable
- useOrientation
- usePageleave
- usePermission
- usePrevious
- usePreviousDistinct
- usePromise
- useQueue
- useRaf
- useRafLoop
- useRafState
- useRenderCount
- useScratch
- useScroll
- useScrollbarWidth
- useScrolling
- useSearchParam
- useSessionStorage
- useSetState
- useShallowCompareEffect
- useSize
- useSlider
- useSpring
- useStartTyping
- useStateList
- useStateValidator
- useStateWithHistory
- useThrottle
- useThrottleFn
- useTitle
- useTween
- useUnmount
- useUnmountPromise
- useUpdate
- useUpdateEffect
- useUpsert
- useVibrate
- useVedio
- useWait
- useWindowScroll
- useWindowSize
- useAdjustColor
- useDarkMode
- useEventListner
- useDimensions
- useFullScreen
- useGeoLocation
- useLsClient
- useKeyPress
- useMultiKeyPress
- useNotification
- useOnClickOutside
- useOnlineStatus
- usePrevious
- usePrint
- useQueryParams
- useSpeech
- useSpeechRecognition
- useSpeechSynthesis
- useWorker
Website—– like stackui
Lang use —– typescript
Npm package launch
Presentation
Code — private github
Project create reference :-
https://www.smashingmagazine.com/2020/05/typescript-modern-react-projects-webpack-babel/