At present, componentized development has become the mainstream of the front. The three frameworks (Vue, React, Angular) are the best leaders in component-based development.

There are many articles in the community telling us how to do component partitioning and what principles to follow. And you must have learned something of your own about component partitioning during the course of the project.

All of this, in the end, teaches us how to split components better to make them more reusable.

But I have a question for you, what is a component?

A component is a combination of UI+ logic.

This means that no matter how you split the components, you end up encapsulating the UI and logic together.

For antD’s Collapse, the built-in UI styles and logic code are a black box for users to Collapse, with limited customization changes.

The problem with this is that when the user needs to customize the UI part, they may need to write another component, and the logic of the two components may be the same. The only difference is in the UI presentation.

In addition, some basic logic code, such as obtaining the real-time mouse position, obtaining the real-time screen size, obtaining the hover state, etc., may be everywhere in our project.

So is there a way to encapsulate the basic logic that is often used in the development process and hand over UI rendering to specific users to achieve greater freedom?

High order components, Render Props

React had two ways to do this before React Hooks came along: high-order components, Render Props.

1. Render Props

For example, let’s say we want to get the real-time position of the mouse and do some business. Part of the code is as follows:

<Mouse>
  (mouse) => (<MyComponent mouse={mouse} />)
</Mouse>
Copy the code

The specific use of Render Props is not described here.

We wrapped the logic to get the Mouse position in the Mouse component, and then returned the Mouse position data by mounting a function on the props as an argument. This way the component can do whatever it wants with the mouse that it gets.

2. Advanced components

Part of the code is as follows:

 functionWithMouse (WrappedComponent) {// getMouse position const mouse = getMouse()return class extends React.component {
     ...
     render() {// Returns the wrapped componentreturn<WrappedComponent mouse={mouse} {... this.props}> } } }Copy the code

When used it might look something like this:

  const MyComponentWithMouse = withMouse(MyComponent)
Copy the code

Now if the requirement becomes: knowing the mouse position, also know window.size. We need to create another component that gets window.size. The code might look like this:

<WindowSize>
  (size) => (
    <Mouse>
      (mouse) => (<MyComponent size={size} mouse={mouse} />)
    </Mouse>
  )
</WindowSize>
Copy the code

The above code could easily make you think of nesting hell, which is intolerable. A similar problem exists with higher-order components. This may be a very important reason why logical reuse does not catch on.

React Hooks

React Hooks solve the logic reuse problem. There are also numerous Hooks libraries in the community.

With Hooks, Custom Hooks make it easy to wrap logic and use it just like normal Hooks without requiring any additional code.

React use. Or above to obtain the mouse position information to illustrate. See how React Use defines this hooks.

const useMouse = (ref: RefObject<Element>): State => {
  const [state, setState] = useRafState<State>({
    docX: 0,
    docY: 0,
    posX: 0,
    posY: 0,
    elX: 0,
    elY: 0,
    elH: 0,
    elW: 0,
  });

  useEffect(() => {
    const moveHandler = (event: MouseEvent) => {
      if (ref && ref.current) {
        const { left, top, width: elW, height: elH } = ref.current.getBoundingClientRect();
        const posX = left + window.pageXOffset;
        const posY = top + window.pageYOffset;
        const elX = event.pageX - posX;
        const elY = event.pageY - posY;

        setState({ docX: event.pageX, docY: event.pageY, posX, posY, elX, elY, elH, elW, }); }}; document.addEventListener('mousemove', moveHandler);

    return () => {
      document.removeEventListener('mousemove', moveHandler);
    };
  }, [ref]);

  return state;
};

export default useMouse;
Copy the code
  1. The Mousemove event is first registered in useEffect
  2. We then get the mouse position information in the callback function moveHandler and update the state
  3. Return the latest state
  4. Remove the Mousemove event when the component is unloaded

Since this part of the logic is almost universal, we don’t need to worry about the implementation logic. In the specific business, we only care about what location information is returned, and then do specific business processing according to the returned results.

So, let’s focus on usage, which is what we need to focus on.

import {useMouse} from 'react-use';

const Demo = () => {
  const ref = React.useRef(null);
  const mouse = useMouse(ref);

  return (
    <div ref={ref}>
      <div>Mouse position is: {JSON.stringify(mouse)} </div>
    </div>
  );
};
Copy the code

This is the only line of core code

const mouse = useMouse(ref);
Copy the code

Compare that to the higher-order functions and Render Props mentioned above.

More importantly, we don’t have to worry about the hierarchy between components. We can use them where we need them. And developers can focus on business-relevant code, which can greatly improve development efficiency.

The community library

React-use is a popular library in the community that contains a lot of basic logic. You’ll definitely need it in your development.

Ahooks is a react-use library that contains Hooks, some of which are based on react-use.

Many of the hooks libraries in the community have the same code because the underlying logic is almost always the same.

Ahooks has a much richer documentation and API specification than the other libraries, as well as several hooks that are not available in other libraries. In particular, the use of useRequest, compared to the use of useAsync in react-use, adds polling, parallel requests, anti-shake, throttling and other processing.

Additionally, useRequest can use its own request library by configuring the requestMethod parameter. For example, using AXIos to send a request, the default is using FETCH.

import { useRequest } from 'ahooks';
import React from 'react';
import axios from 'axios';
export default () => {
  const { data, error, loading } = useRequest('https://helloacm.com/api/random/?n=8&x=4', {
    requestMethod: (param: any) => axios(param),
  });
  if (error) {
    return <div>failed to load</div>;
  }
  if (loading) {
    return<div>loading... </div>; }return<div>Number: {data? .data}</div>; };Copy the code

You can also set global options on the outermost layer of the project via the UseRequestProvider.

import { UseRequestProvider } from 'ahooks';
export function ({children})=>{
  return (
    <UseRequestProvider value={{
      refreshOnWindowFocus: true,
      requestMethod: (param)=> axios(param),
      ...
    }}>
      {children}
    </UseRequestProvider>
  )
}
Copy the code

conclusion

Finally, for syntactic reasons of the other two languages, there is no easy way to share logic like React Hooks.

But Hooks give us a good idea and show us the community’s enthusiasm and need for logic sharing. So I believe there will be a convenient way to share logic for Angular and Vue in the near future.

By then, all the underlying common logic may have been written for you. Developers can really focus on the code that is relevant to the business logic.