In class based components was easy to perform a deep comparison. componentDidUpdate provides a snapshot of previous props and previous state
componentDidUpdate(prevProps, prevState, snapshot){
    if(prevProps.foo !== props.foo){ /* ... */ }
} 
The problem is useEffect it's not exactly like componentDidUpdate. Consider the following
useEffect(() =>{
    /* action() */
},[props])
The only thing you can assert about the current props when action() gets called is that it changed (shallow comparison asserts to false). You cannot have a snapshot of prevProps cause hooks are like regular functions, there aren't part of a lifecycle (and don't have an instance) which ensures synchronicity (and inject arguments). Actually the only thing ensuring hooks value integrity is the order of execution.
Alternatives to usePrevious
Before updating check if the values are equal
   const Component = props =>{
       const [foo, setFoo] = useState('bar')
       const updateFoo = val => foo === val ? null : setFoo(val)
   }
This can be useful in some situations when you need to ensure an effect to run only once(not useful in your use case).
useMemo:
If you want to perform comparison to prevent unecessary render calls (shoudComponentUpdate), then  useMemo is the way to go
export React.useMemo(Component, (prev, next) => true)
But when you need to get access to the previous value inside an already running effect there is no alternatives left. Cause if you already are inside useEffect it means that the dependency it's already updated (current render). 
Why usePrevious works
useRef isn't just for refs, it's a very straightforward way to mutate values without triggering a re render. So the cycle is the following
- Componentgets mounted
- usePreviousstores the inital value inside- current
- propschanges triggering a re render inside- Component
- useEffectis called
- usePreviousis called
Notice that the usePrevious is always called after the useEffect (remember, order matters!). So everytime you are inside an useEffect the current value of useRef will always be one render behind.
const usePrevious = value =>{
    const ref = useRef()
    useEffect(() => ref.current = value,[value])
}
const Component = props =>{
    const { A } = props
    useEffect(() =>{
        console.log('runs first')
    },[A])
    //updates after the effect to store the current value (which will be the previous on next render
    const previous = usePrevious(props)
}