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.

  • 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

  • react

    • useAppEvent

      • Allows functional component to subscribe to application lifecycle events. Probably don’t use this.

paragon

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

See Testing Recipes

 

Example time: Learner dash thunk-killing POC PR

Thunk Removal Proof-of-concept by muselesscreator · Pull Request #98 · openedx/frontend-app-learner-dashboard