React hooks are taking over and useEffect is one of the most popular. This hook is very versatile and replaces multiple life cycle methods. This versatility is also fairly misunderstood. In this article, I will cover 3 complementary hooks to useEffect .

usePrevious hook

One of the shortcomings of useEffect is that there is no out of the box solution for accessing previous values. The suggested solution is to store the current value of a watched variable after each render (using useRef). On the next render, this value can be accessed and compared with the current value.

function Counter() {

const [count, setCount] = useState(0);



const prevCountRef = useRef();

useEffect(() => {

prevCountRef.current = count;

});

const prevCount = prevCountRef.current;



return <h1>Now: {count}, before: {prevCount}</h1>;

}

The snippet above is copied straight from here. In the example above useRef hook returns an object that is persisted until the component is unmounted. Every time the component is rendered the last value of the count variable is stored. It would be accessible as the previous value in the next render.

The logic for storing watched value can be extracted to a hook (also suggested here) as shown below.

function Counter() {

const [count, setCount] = useState(0);

const prevCount = usePrevious(count);

return <h1>Now: {count}, before: {prevCount}</h1>;

}



function usePrevious(value) {

const ref = useRef();

useEffect(() => {

ref.current = value;

});

return ref.current;

}

A more common use of this is for conditional effects where we need to compare current and previous values. Here is an example below.

function MyComponent({value}) {

const previousValue = usePrevious(value);

useEffect(() => {

if(previousValue !== value) {

// do some magic

}

},[value]);

// ...

}

useMountEffect hook

The useEffect hook takes a second argument, an array of values to watch for changes. The effect function would be run the first render whenever these values change. A result of this is that when an empty array is passed in as a second argument there are no changed values, so the effect is applied only once. This is perfect for initialization code when a component is first mounted.

A good practice is to use a different effect for different concerns. This can lead to multiple useEffect functions in your component function. I think a named hook for the initialization required. Here is where the useMountEffect comes in (It is also mentioned here ). This is for those missing the componemtDidMount life cycle method. Having a custom hook has the advantage of hinting the intention of the hook, and in my opinion, is more intuitive. Here is our hook below.

function useMountEffect(effectFn, useEffect(effectFn,[]));

useChangeEffect hook

As mentioned earlier the effect function would run after the first render then every time the passed values in the second argument change. Ok, what if I did not want to run the effect function on the first render, but only when the watched values change? This where the useChangeEffect comes to the rescue. See hook below.

function useChangeEffect(effectFunction, values) {

const previousValues = useRef(values);

useEffect(() => {

previousValues.current = values;

}, values); useEffect(() => {

const changed = values.some((value, index) => lastValue.current && !Object.is(value, lastValue.current[index])); if(changed){

return effectFunction();

}

}, values);

};

The same function updated to use usePrevious.

function useChangeEffect(effectFunction, values) {

const previousValues = usePrevious(values);

useEffect(() => {

const changed = values.some((value, index) => previousValues && !Object.is(value, previousValues[index]));

if(changed){

return effectFunction();

}

}, values);

}

This hook watches for changed values. It wraps the effect function with a function that compares the old and new values before calling the original effect function. The first render would always be skipped since the previous values are undefined on the first render.

To conclude

React useEffect hook is a very flexible hook. However, it has some shortcomings that should not stop us. There is always the option to use custom hooks. Hopefully, new hooks would be added to React core.