← Home

An attempted simple guide for react hooks.

@date=2020-05-14
@tags=react, programming, guides

2020-05-14-14-46-05.png

React hooks is what got me to actually like React enough to make it my primary web development framework (at least at this date). They break away from the ugly class based heavily nested object form of React, and make it truly more functional, while giving allowances for managing state in a stateless system.

The first thing to grasp about react is that components re-render with every change in the props that come into them, and thus every time they are run (which is every time an input changes), all internal values are wiped out. It should also be noted that internal hook state changes also trigger these re-renders.

The following hooks help keep all of this from getting washed away with each re-render so that your components can continue to function.

useState

This one you will likely use the most.

const [state, setState] = useState(initialState);

Sets a value for the initial state, and gives you something to update that state with setState.

Updating the state will cause a rerender, but the state value will be maintained.

Note that the left hand side of the equal sign is an array, not an object. state and setState are variables that you make up, and could be called anything, and it is really the order of the outputs from useState that causes state and setState to be set correctly. For example:

const [burgersAreDoneCooking, setBurgersAreDoneCooking] = useState(false);

Would be how you would track the state of the burgers in your cooking app.

useContext

This can be used to subscribe to a context value set higher up in the tree.

function Grandparent() {
  const [favoriteColor, setFavoriteColor] = useState('blue');
  return (
    <FavoriteColor.Provider value={favoriteColor}>
      <Parent />
    </FavoriteColor.Provider>
  );
}
function Parent(){
  return (
    <div>
      <Grandchild />
    </div>
  );
}
function Grandchild(){
   const grandpasFavColor = useContext(FavoriteColor);
   return (<div>My grandpa's favorite color is {grandpasFavColor}</div>)
}

useReducer

Outputs a state and a dispatcher. The dispatch is a function that will call the reducer with the current state and an action, and that reducer then returners an entirely new object that is a mutation of the original state to provide a new state value.

const initialState = {count: 0, otherValues:['a', 'b', 'c']};

function countReducer(state, action){
  switch (action.type) {
    case 'increment':
      return {...state, count:state.count+1};
    case 'decrement':
      return {...state, count:state.count-1};
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type:'decrement'})}>-</button>
      <button onClick={() => dispatch({type:'increment'})}>+</button>
    </>
  );
}

useEffect

This is run after the page renders, and if you leave out the dependency array, it will rerun every single time. Usually you only want this to run once though, so setting it to depend on some variables changing, or at a minimum, just passing it [] as the second parameter will keep it from repeatedly running

useEffect(
  () => {
    /*Do something just once after rendering, unless depends array changes*/
  },
  [somethingToDependOn],
);

useMemo

Only re-run when the dependency changes. This is useful for expensive calculations. It can be used as a watcher for a dependency that will then cause a function to run.

largestPrime = useMemo(
() => calculateLargestPrimeOfNumber(newNumber),
[newNumber]
);

This will update the messageHistory every time a new value comes in for lastMessage

useRef

const refContainer = useRef(initialValue);

Returns an object with a property .current that you can mutate whenever. The object will persist and so you get to keep access to current. This is sometimes used with

<div ref={yourRefObj}>. When react rerenders the dom, it will reapply your object to it, and your yourRefObj.current will always point to the dom element

useCallback

Stores a callback on a variable so you can use it on children elements and won't cause a render of the children for every time the parent changes. Since in the below, the reference to onChildChange always stays the same on re-renders, it makes it so <Child> doesn't detect a change in it's passed in values.

We use "a" as a dependency so that the callback is updated when the state of "a" is updated.

function Parent({ ... }) {
  const [a, setA] = useState(0);
  const onChildChange = useCallback(() =>
   {doSomething(a);}, [a]);
  ...
  return (
  ...
    <Child onChange={onChildChange} >
  );
}

Some others

I skipped useImperativeHandle, useLayoutEffect, useDebugValue as those are rarely used. You can read the documentation about them here.