(although they're now available with other frameworks)
First read the Rules of Hooks
OK, now let's go...
const something[s]ToHelpYouUseIt? = hookFunction(arguments);
When you call the hookFunction you pass it [generally 'starting] arguments'/defaults it needs to setup
You get them all from the react package:
import React, { useState, useEffect, etc } from 'react';
Replaces state we get from classes
const [count, setCount] = useState(0);
You use it like so:
So, from our Anatomy of hooks...
const something[s]ToHelpYouUseIt? = hookFunction(arguments);
Replaces componentDidMount & componentDidUpdate [& componentWillUnmount]
useEffect(/* Callback */, [/* dependancies */]
You use it like so:
So practically
useEffect(
// Callback
() => {
// Do something on mounting/updating
const handler = setTimeout(() => {
// just using a timeont as an example because it needs cleanup
}, delay);
// [optionally] Clean up when done (componentWillUnmount)
return () => {
clearTimeout(handler);
};
},
// Dependancies:
[value, delay] // Only re-run effect if these values change
);
There is a rule in the eslint package that will help you (react-hooks/exhaustive-deps)
A nicer way of using the context API
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
const memoizedValue = useMemo(
() => computeExpensiveValue(a, b),
[a, b]
);
function TextInputWithFocusButton() {
const inputEl = useRef();
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
import { useRef, useImperativeHandle, forwardRef}
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
export default FancyInput
// useEffect(() => {
useLayoutEffect(() => {
listRef.current.scrollTop = listRef.current.scrollHeight;
});
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
// ...
// Show a label in DevTools next to this Hook
// e.g. "FriendStatus: Online"
useDebugValue(isOnline ? 'Online' : 'Offline');
return isOnline;
}
function NameFields() {
const id = useId();
return (
<div>
<label htmlFor={id + '-firstName'}>First Name</label>
<div>
<input id={id + '-firstName'} type="text" />
</div>
<label htmlFor={id + '-lastName'}>Last Name</label>
<div>
<input id={id + '-lastName'} type="text" />
</div>
</div>
);
}
function Typeahead() {
const query = useSearchQuery('');
const deferredQuery = useDeferredValue(query);
// Memoizing tells React to only re-render when deferredQuery changes,
// not when query changes.
const suggestions = useMemo(() =>
<SearchSuggestions query={deferredQuery} />,
[deferredQuery]
);
return (
<>
<SearchInput query={query} />
<Suspense fallback="Loading results...">
{suggestions}
</Suspense>
</>
);
}
const [searchTerm, setSearchTerm] = useState("");
const [data, setData] = useState(USERS);
const [isPending, startTransition] = useTransition();
function handleChange(value) {
setSearchTerm(value); // more important
startTransition(() => { // less important
setData(filterUsers(value));
});
}
return (
<>
<input value={searchTerm} onChange={handleChange} />
{isPending ? <p>Updating list</p> : null}
<PeopleList people={data} />
</>
)