Recently, as the project came to an end, I finally had time to summarize this work. In fact, Vue has been used before, but now React is the main business of the company. I have read many articles specially for this purpose. In addition, there are many similarities between the two frameworks, so this article came into being.

As the main purpose is to share experience, the following examples are mainly used as a primer. Before the formal sharing, we will introduce two little knowledge

  • Hooks by default to use as the prefix, this is the official recommendation in the React, constraint is greater than the configuration and convenient eslint analytical tools such as identification, here also follow this way;
  • The main difference between watchEffect and useEffect is that watchEffect automatically detects dependencies, while useEffect requires you to specify;
const count = ref(0);
const onClick = () = > {
  count.value++;
};
watchEffect(() = > {
  console.log('Clicked');
});
Copy the code

For example, it is important to remember that watchEffect will not be repeated when onClick is triggered because it has an internal process to collect dependencies and will only be re-executed if the dependencies change.

The sample

There are three types based on common scenarios. If you have better examples, please add them

DOM

Modify page title

import { ref, watchEffect } from 'vue';

const useTitle = (title) = > {
  const str = ref(title);
  watchEffect(() = > {
    document.title = str.value;
  });
};
Copy the code

Call useTitle to update the title

Listen for page size changes

import { watchEffect, reactive, toRefs } from 'vue';

const useResize = () = > {
  const size = reactive({
    width: window.innerWidth,
    height: window.innerHeight,
  });
  const onChange = () = > {
    Object.assign(size, {
      width: window.innerWidth,
      height: window.innerHeight,
    });
  };
  watchEffect((onInvalidate) = > {
    window.addEventListener('resize', onChange);
    onInvalidate(() = > {
      window.removeEventListener('resize', onChange);
    });
  });
  return toRefs(size);
};
Copy the code

The reason toRefs is called here is so that deconstruction situations can also be used, for example

const { width } = useResize();
Copy the code

Listen to whether the network is disconnected

import { ref, watchEffect } from 'vue';

const useLineState = () = > {
  const line = ref(window.navigator.onLine);
  const onLine = () = > {
    line.value = true;
  };
  const onOffline = () = > {
    line.value = false;
  };

  watchEffect((onInvalidate) = > {
    window.addEventListener('online', onLine);
    window.addEventListener('offline', onOffline);
    onInvalidate(() = > {
      window.removeEventListener('online', onLine);
      window.removeEventListener('offline', onOffline);
    });
  });

  return line;
};
Copy the code

Similar to the useResize above, listen for related events to determine the related state of state

Listen for DOM element changes

import { watchEffect, reactive, toRefs } from 'vue';

const isObject = (obj) = > typeof obj === 'object' && obj;
const isElement = (obj) = > isObject(obj) && obj.nodeType === Node.ELEMENT_NODE;
const useResizeObserver = (dom) = > {
  if(! isElement(dom)) {throw new Error(`DOM is not an element! `);
  }
  const size = reactive(dom.getBoundingClientRect());
  watchEffect((onInvalidate) = > {
    const resizeObserver = new ResizeObserver(() = > {
      Object.assign(size, dom.getBoundingClientRect());
    });
    onInvalidate(() = > {
      resizeObserver.disconnect();
    });
  });
  return toRefs(size);
};
Copy the code

Here, the listener element is ResizeObserver, which is still in the experimental stage. Please use Polyfill for compatibility

Encapsulates the request

Ajax requests are common, but in vue2. X we could easily write the following code

mounted(() = > {
  fn().then().catch().finally();
});
Copy the code

The biggest problem with this code is that it’s not clear, because variables are defined in data, and it looks a lot more intuitive if you switch to the following form.

Further examples can also combine forms and tables for deeper hooks encapsulation

const { data, loading, error } = useRequest(() = > {
  / /...
});
if (loading) {
  / /...
}
if (error) {
  // ...
}
Copy the code
import { watchEffect, reactive, toRefs } from 'vue';

const useRequest = (fn, { manual } = {}) = > {
  const obj = reactive({
    loading: false.data: undefined.error: undefined});const run = () = > {
    obj.loading = true;
    Promise.resolve(fn())
      .then((data) = > {
        obj.data = data;
      })
      .catch((err) = > {
        obj.error = err;
      })
      .finally(() = > {
        obj.loading = false;
      });
  };
  watchEffect(() = > {
    if (!manual) {
      run();
    }
  });

  return {
    ...toRefs(obj),
    run,
  };
};
Copy the code

Simulation life cycle

The official Vue lifecycle is quite complete, but some of the lifecycle can be simulated by watchEffect.

Mounted and beforeUnmount are examples of mounted and beforeUnmount. The watchEffect is used to automatically detect dependencies

import { watchEffect, nextTick } from 'vue';

const useMounted = (fn) = > {
  watchEffect(() = > {
    if (typeoffn ! = ='function') {
      return;
    }
    nextTick().then(() = > {
      fn();
    });
  });
};

const useBeforeUnmount = (fn) = > {
  watchEffect((onInvalidate) = > {
    onInvalidate(() = > {
      if (typeoffn ! = ='function') {
        return;
      }
      fn();
    });
  });
};
Copy the code

Encapsulated persistence

In some login pages, it is easy to see and remember the relevant account and password. The traditional approach is to store the relevant account and password after login, and then check whether the relevant localStorage has corresponding data during page rendering.

LocalStorage: which supports hooks on objects? LocalStorage: which supports hooks on objects

import { ref, watchEffect } from 'vue';
const isObject = (obj) = > typeof obj === 'object' && obj;
const useLocalStorageState = (key, defaultValue) = > {
  const value = ref(undefined);
  try {
    value.value = window.localStorage.getItem(key);
    if (value.value === undefined) {
      value.value = defaultValue;
    }
    value.value = JSON.parse(value.value);
  } catch {}

  watchEffect(() = > {
    const v = value.value;
    window.localStorage.setItem(key, isObject(v) ? JSON.stringify(v) : v);
  });
  return value;
};
Copy the code
const name = useLocalStorageState('name'.'admin');
Copy the code

The last

The above code has been uploaded to CodesandBox. If it is helpful to you, you can star it

Refer to the article

  • How to Build Wheels with React Hooks