React hooks - custom and common
What are hooks?
Chunk of functionality that is part of that component that is being initialized within the component.
Some additional benefits of using hooks:
reusability
testability
Hooks must be included at top of components.
Additional “Rules of Hooks”: Rules of Hooks – React
Hooks should be designers that they are always called, even if the returned output isn’t necessary used.
const useMyEvent = (initializationArgs) => { ...doStuffWithArgs; return (e) => { ... eventHandler }; }
Common React Hooks
React
useState
Defines a state value and setter, with an initial value. Every time it is called, it checks the current value.
When testing, easiest to pull out into a separate object so that you can isolate and test the behavior of each state value.
useEffect
Replacement for lifecycle methods.
Can be created with prerequisites to limit when they get run
empty list means only run on first load (you will need an
eslint-disable-line
comment)passing a list of prereqs means that the hook will only run when one of those values changes.
useEffect(() => { doThinkOnLoad(); }, []); useEffect(() => { doThinkWhenValueChanges(myValue); }, [myValue]);
useCallback
creates a memoized callback for memory-intensive callback methods. Should generally be avoided unless a method is actively impacting performance, as the useCallback overhead is more computationally intensive than it is generally worth.
useRef
creates a ref object to use in react components. I tend to find these add a bit of instability and are trickier to scale, so I try to avoid them where I can in my code.
Allows you to get access to an underlying DOM element within the component, e.g. for doing focus state manipulations, etc.
useRef
can also be used as “state”, but without triggering component re-renders.
useContext
Provides access to fields in a Context object.
Whenever any part of the context value changes, all the children nested under the context provider re-render.
More hooks and info about Context will come later in a talk from @Adam Stankiewicz .
const { authenticatedUser, config } = useContext(AppContext)
useReducer
An alternative to
useState
when you have more complex state logic.Accepts a reducer function along with an initial state, and returns current state paired with a
dispatch
method.Helps you write vanilla React a bit more Redux-like (e.g., dispatching actions), especially when used in conjunction with
useContext
.
React-redux
useSelector
provides easy access to redux state, especially with simple selectors that take only state as an argument.
useDispatch
Returns the dispatch object for dispatching redux actions.
Suggested Pattern - top-level redux hooks
I recommend setting up a redux hooks file near the top of your app. This should provide hooks to any selectors and actions (including network and complex actions) that components will need. This will prevent you from needing to connect any components to react-redux mechanisms directly, and provides you a clean interface for controlling and testing your redux hooks.
Fun Note (Thunk):
It is worth noting here, that this functionality effectively removes the need for thunk middleware in most cases, as its core use cases of “perform multiple connected actions” and “perform network actions tied to the redux store” are now easily available in hooks.
frontend-platform
i18n
useIntl
react
useAppEvent
Allows functional component to subscribe to application lifecycle events. Probably don’t use this.
paragon
useCheckboxSetValues
Return the values from a checkbox set with defaults, providing set modification methods.
@Adam Stankiewicz TODO: file ticket to add explicit page to Paragon docs site for this hook.
useWindowSize
Loads width and height of the current view window
useMediaQuery
Especially for breakpoints
GitHub - yocontra/react-responsive: CSS media queries in react - for responsive design, and more.
useToggle
Toggle a boolean value on or off with handlers
https://github.com/openedx/paragon/blob/master/src/hooks/useToggle.mdx
useIndexOfLastVisibleChild
This hook will find the index of the last child of a containing element that fits within its bounding rectangle. This is done by summing the widths of the children until they exceed the width of the container.
https://github.com/openedx/paragon/blob/master/src/hooks/useIndexOfLastVisibleChild.jsx
react-table
Used by
DataTable
TODO: We will have to consider upgrading to react-table v8.
What impacts does this have for us?
Should not have to use these react-table hooks as a consumer of
@edx/paragon
useTable
useFilters
useGlobalFilter
useSortBy
useGroupBy
useExpanded
usePagination
useRowSelect
useRowState
useColumnOrder
useResizeColumns
useBlockLayout
useAbsoluteLayout
useFlexLayout
Custom React Hooks
When to make something a hook
Component behavior that does something beyond calling other custom hooks for values. This includes things like managing state and creating effects/callbacks.
Should be a hook
Fine to leave in component
Crafting your hooks
One of the core design principles behind hooks it the extraction of behavior from react components for the purpose of re-use. To this end, wherever possible, try to prioritize small, re-useable hooks that define the behavior that they are encapsulating.
Hooks that provide behavioral logic around a component can generally be named for the component (MyHookComponent, useMyHookComponentData()). But look for places where you can sub-bundle the logic if these get too complex.
App Strategy: elevate app-level hooks
With the advent of react-redux hooks, we no longer really need to connect components to redux directly in the old paradigm. Now, we are able to define hooks that connect the redux store to our components.
I recommend defining these at the app-level, and using them liberally through your app.
So, we define:
a redux reducer and the actions to modify
a set of selectors to define access to that data
a set of pass-through hooks that provide access to the selectors and actions.
Another set of top-level hooks that wrap network events, with access to redux-level data.
How to test your hooks