Hooks are a new feature since Act 16.8. While the React documentation explains the concepts related to hooks, it is difficult to use them from the official documentation. It is easy to jump into traps and errors when writing hooks. This article summarizes five downsides.

01. Use it without renderuseState

We can use useState to manage state in function components, which makes it easy to manage state, but also easy to abuse. Let’s look at some overlooked areas in the following code sample.

X is not recommended

function ClickButton(props){
  const [count, setCount] = useState(0)
  const onClickCount = () = > {
    setCount((c) = > c + 1)}const onClickRequest = () = > {
    apiCall(count)
  }
  
  return (
    <div>
      <button onClick={onClickCount}>Click</button>
      <button onClick={onClickRequest}>Submit</button>
    </div>)}Copy the code

The problem: If you take a closer look at the code above, there’s nothing wrong at first glance. Click the button to update count. SetCount does not use the count state in the return part, and every setCount will cause the component to render again, which is not needed. The extra render will make the page performance worse, so we can modify the code as follows:

√ If we simply want a variable that will last through the component declaration cycle, but the update of the variable does not require a re-rendering of the component, we can use the useRef hook.

function ClickButton(props){
  const count = useRef(0)
  const onClickCount = () = > {
    count.current++
  }
  const onClickRequest = () = > {
    apiCall(count.current)
  }

  return (
    <div>
      <button onClick={onClickCount}>Click</button>
      <button onClick={onClickRequest}>Submit</button>
    </div>)}Copy the code

02. Userouter.pushRather thanlink

In React SPA, we use the React router to handle route redirects. We usually have a button in the component that handles route redirects by clicking the button:

X is not recommended

function ClickButton(props){
  const history = useHistory()
  const onClickGo = () = > {
    history.push('/where-page')}return <button onClick={onClickGo}>Go to where</button>
}
Copy the code

The problem: Although the code above works, it doesn’t measure Accessibility by design. This type of button is not treated as a link that screen readers can jump to. So we can modify the code as follows:

Recommended)

function ClickButton(props){
  return <Link to="/next-page">
    <span>Go to where</span>
  </Link>
}
Copy the code

03. ThroughuseEffectTo deal with the actions

Sometimes we just want to run some extra code after React updates the DOM. Such as sending network requests, manually changing the DOM, and logging.

X is not recommended

function DataList({ onSuccess }) {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [data, setData] = useState(null);

  const fetchData = () = > {
    setLoading(true);
    callApi()
      .then((res) = > setData(res))
      .catch((err) = > setError(err))
      .finally(() = > setLoading(false));
  };

  useEffect(() = >{ fetchData(); } []); useEffect(() = > {
    if(! loading && ! error && data) { onSuccess(); } }, [loading, error, data, onSuccess]);return <div>Data: {data}</div>;
}
Copy the code

The problem: The above code uses two USEeffects, the first to request asynchronous data and the second to invoke the callback function. The execution of the second useEffect will not be triggered until the first asynchronous request data is successful. However, we cannot guarantee that the dependencies of the second useEffect will be completely controlled by the first useEffect successful request data. So we can modify the code as follows:

Recommended)

function DataList({ onSuccess }) {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [data, setData] = useState(null);

  const fetchData = () = > {
    setLoading(true);
    callApi()
      .then((res) = > {
        setData(res)
        onSuccess()
       })
      .catch((err) = > setError(err))
      .finally(() = > setLoading(false));
  };

  useEffect(() = >{ fetchData(); } []);return <div>Data: {data}</div>;
}
Copy the code

04. Single responsibility component

When should you split a component into smaller components? How do I build a component tree? All of these issues arise daily when using component-based frameworks. However, a common mistake when designing components is to combine two use cases into one component.

X is not recommended

function Header({ menuItems }) {
  return (
    <header>
      <HeaderInner menuItems={menuItems} />
    </header>
  );
}

function HeaderInner({ menuItems }) {
  return isMobile() ? <BurgerButton menuItems={menuItems} /> : <Tabs tabData={menuItems} />;
}
Copy the code

Why it’s a problem: By doing this in the code above, the component HeaderInner tries to be two different things at once, and doing more than one thing at a time isn’t ideal. In addition, it makes it more difficult to test or reuse components elsewhere. So we can modify the code as follows:

Recommended)

Raising the criteria one level makes it easier to see what components are for, and they have only one responsibility,
or
, rather than trying to be two different things at once.

function Header(props) {
  return (
    <header>
      {isMobile() ? <BurgerButton menuItems={menuItems} /> : <Tabs tabData={menuItems} />}
    </header>)}Copy the code

05. Single function useEffects

By comparing componentWillReceiveProps or componentDidUpdate method, realize the userEffect beauty. However, improper useEffect can also cause problems.

X is not recommended

function Example(props) {
  const location = useLocation();
  const fetchData = () = > {
    /* Calling the api */
  };

  const updateBreadcrumbs = () = > {
    /* Updating the breadcrumbs*/
  };

  useEffect(() = > {
    fetchData();
    updateBreadcrumbs();
  }, [location.pathname]);

  return (
    <div>
      <BreadCrumbs />
    </div>
  );
}
Copy the code

The problem: The useEffect above triggers two side effects at the same time, but not both of them are the desired side effects, so we can modify the code to look like this:

Recommend √ Separate two side effects from one useEffect.

function Example(props) {
  const location = useLocation();

  const fetchData = () = > {
    /* Calling the api */
  };

  const updateBreadcrumbs = () = > {
    /* Updating the breadcrumbs*/
  };

  useEffect(() = > {
    updateBreadcrumbs();
  }, [location.pathname]);

  useEffect(() = >{ fetchData(); }, [])return (
    <div>
      <BreadCrumbs />
    </div>
  );
}
Copy the code

Five common mistakes writing React Components (with hooks) in 2020