前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Hooks】:[组]Awesome React Hooks

【Hooks】:[组]Awesome React Hooks

作者头像
WEBJ2EE
发布2021-02-26 16:10:29
6940
发布2021-02-26 16:10:29
举报
文章被收录于专栏:WebJ2EE
代码语言:javascript
复制
目录
1. Making Sense of React Hooks?
  1.1. Why Hooks?
  1.2. Do Hooks Make React Bloated?
  1.3. What Are Hooks, Exactly?
  1.4. Show Me Some Code!
2. What's going to happen to render props?
3. Migrating from lifecycle methods to hooks
  3.1. From componentDidMount to useMount
  3.2. From componentWillUnmount to useUnmount
  3.3. From componentDidUpdate to useUpdate
  3.4. From shouldComponentUpdate to memo and useMemo
  3.5. From getDerivedStateFromProps to… render
  3.6. From componentDidCatch to… nothing

1. Making Sense of React Hooks?

1.1. Why Hooks?

We know that components and top-down data flow help us organize a large UI into small, independent, reusable pieces.However, we often can’t break complex components down any further because the logic is stateful and can’t be extracted to a function or another component. Sometimes that’s what people mean when they say React doesn’t let them “separate concerns.”

These cases are very common and include animations, form handling, connecting to external data sources, and many other things we want to do from our components. When we try to solve these use cases with components alone, we usually end up with:

  • Huge components that are hard to refactor and test.
  • Duplicated logic between different components and lifecycle methods.
  • Complex patterns like render props and higher-order components.

We think Hooks are our best shot at solving all of these problems.Hooks let us organize the logic inside a component into reusable isolated units:

Hooks apply the React philosophy (explicit data flow and composition) inside a component, rather than just between the components. That’s why I feel that Hooks are a natural fit for the React component model.

Unlike patterns like render props or higher-order components, Hooks don’t introduce unnecessary nesting into your component tree. They also don’t suffer from the drawbacks of mixins.

1.2. Do Hooks Make React Bloated?

If the React community embraces the Hooks proposal, it will reduce the number of concepts you need to juggle when writing React applications. Hooks let you always use functions instead of having to constantly switch between functions, classes, higher-order components, and render props.

The Hooks proposal doesn’t include any breaking changes. Your existing code would keep on working even if you adopted Hooks in the newly written components. In fact, that’s exactly what we recommend — don’t do any big rewrites!

1.3. What Are Hooks, Exactly?

To understand Hooks, we need to take a step back and think about code reuse.

Today, there are a lot of ways to reuse logic in React apps. We can write simple functions and call them to calculate something. We can also write components (which themselves could be functions or classes). Components are more powerful, but they have to render some UI. This makes them inconvenient for sharing non-visual logic. This is how we end up with complex patterns like render props and higher-order components. Wouldn’t React be simpler if there was just one common way to reuse code instead of so many?

Functions seem to be a perfect mechanism for code reuse. Moving logic between functions takes the least amount of effort. However, functions can’t have local React state inside them. You can’t extract behavior like “watch window size and update the state” or “animate a value over time” from a class component without restructuring your code or introducing an abstraction like Observables. Both approaches hurt the simplicity that we like about React.

Hooks solve exactly that problem. Hooks let you use React features (like state) from a function — by doing a single function call. React provides a few built-in Hooks exposing the “building blocks” of React: state, lifecycle, and context.

Since Hooks are regular JavaScript functions, you can combine built-in Hooks provided by React into your own “custom Hooks”. This lets you turn complex problems into one-liners and share them across your application or with the React community:

Note that custom Hooks are not technically a React feature. The possibility of writing your own Hooks naturally follows from the way Hooks are designed.

1.4. Show Me Some Code!

Let’s say we want to subscribe a component to the current window width (for example, to display different content on a narrow viewport).

There are several ways you can write this kind of code today. They involve writing a class, setting up some lifecycle methods, or maybe even extracting a render prop or a higher-order component if you want to reuse it between components. But I think nothing quite beats this:

If you read this code, it does exactly what it says. We use the window width in our component, and React re-renders our component if it changes. And that’s the goal of Hooks — to make components truly declarative even if they contain state and side effects.

Let’s look at how we could implement this custom Hook. We’d use the React local state to keep the current window width, and use a side effect to set that state when the window resizes:

As you can see above, the built-in React Hooks like useState and useEffect serve as the basic building blocks. We can use them from our components directly, or we can combine them into custom Hooks like useWindowWidth. Using custom Hooks feels as idiomatic as using React’s built-in API.

Hooks are fully encapsulated — each time you call a Hook, it gets isolated local state within the currently executing component. This doesn’t matter for this particular example (window width is the same for all components!), but it’s what makes Hooks so powerful. They’re not a way to share state — but a way to share stateful logic. We don’t want to break the top-down data flow!

Each Hook may contain some local state and side effects. You can pass data between multiple Hooks just like you normally do between functions. They can take arguments and return values because they are JavaScript functions.

The ability to pass data between Hooks make them a great fit for expressing animations, data subscriptions, form management, and other stateful abstractions. Unlike render props or higher-order components, Hooks don’t create a “false hierarchy” in your render tree.They’re more like a flat list of “memory cells” attached to a component. No extra layers.

2. What's going to happen to render props?

And React Hooks are WAY simpler than class components + render props.

Here's a typical usage of react-toggled:

代码语言:javascript
复制
function App() {
  return (
    <Toggle>
      {({on, toggle}) => <button onClick={toggle}>{on ? 'on' : 'off'}</button>}
    </Toggle>
  )
}

If all we wanted was simple toggle functionality, our hook version would be:

代码语言:javascript
复制
function useToggle(initialOn = false) {
  const [on, setOn] = useState(initialOn)
  const toggle = () => setOn(!on)
  return {on, toggle}
}

Then people could use that like so:

代码语言:javascript
复制
function App() {
  const {on, toggle} = useToggle()
  return <button onClick={toggle}>{on ? 'on' : 'off'}</button>
}

Cool! A lot simpler!

3. Migrating from lifecycle methods to hooks

3.1. From componentDidMount to useMount

The componentDidMount method is a method that usually triggers side effects, once, when the component is mounted.

The equivalent hook would be something that triggers a side effect when the SFC is called for the first time and stops being called after that. The hook we can use for that is called useEffect . By default, it is called everytime the component is called (i.e. when mounting and updating). If we take a closer look at the documentation, we see that it can be “memoized” by passing an array of values as the second argument. Moreover, passing an empty array will tell React to call it once and never again. An empty array is like saying “there is no value that would make this function result outdated when it changes”. So our componentDidUpdate implementation would simply be:

代码语言:javascript
复制
import { useEffect } from 'react'

function useMount(fn) {
  useEffect(() => void fn(), [])
}

By calling the function with void , we make sure that we return undefined to the hook (we will see why it matters when we try to mimic componentWillUnmount. Since we pass an empty array, React will apply the effect once and never update (as no value is observed by the memoization process). Therefore, it will be called at when the component is mounting.

3.2. From componentWillUnmount to useUnmount

The componentWillUnmount method is also used to trigger side effect, but this time when the component is unmounted. It is not an uncommon case to use it in combination with componentDidMount, for example to create and delete event listeners.

As we saw in the previous chapter, we can prevent our hook from triggering on every render by passing an empty array of value to observe. By doing so we tell it be called once on mounting then never again. But what if we dont want to call him once at mount-time, but call something at unmount-time ? Well, the documentation from useEffect tells us that any function returned by the function called by the useEffect hook will be called when the component is unmounted. So could we could just do something like this:

代码语言:javascript
复制
import { useEffect } from 'react'

function useUnmount(fn) {
  useEffect(() => fn, [])
}

By calling our arrow function, we do nothing and return the reference to the function we want React to call when it unmounts our component. Once again, passing an empty array ensure we do not repeatedly call our function for every render (since it does nothing anyway, it would be a waste of ressources).

3.3. From componentDidUpdate to useUpdate

Last but not least, we would like to mimic the behaviour of componentDidUpdate, which is to be called on every rendering except the initial one.

We saw in the previous examples that useEffect takes a second parameter that tells to call the function we pass under certain condition. By not passing this argument, we tell React that ”nothing should ever prevent this effect from being called at render”. That includes the first one, so we need to take care of this one in particular but the rest of the time, React will call our effect regardless of any condition. For the initial rendering, we would need to declare a value (a boolean) that tells us if we’re in the initial rendering or any subsequent one. This value should not trigger and new rendering when updated and should be accessible from our useUpdate function.

This is a job that can be fulfilled by the useRef method. Think of useRef as a useState that does not trigger a re-rendering of our component. So all we need to do is tweak our useMount function a little:

代码语言:javascript
复制
import { useRef, useEffect } from 'react'

function useUpdate(fn) {
  const mounting = useRef(true)
  useEffect(() => {
    if (mounting.current) {
      mounting.current = false
    } else {
      fn()
    }
  })
}

In the code above we:

  • Create a ref using the useRef hook. This allows us to store our boolean somewhere we can access between successive calls of our hook.
  • Always call the effect. This is one of the two rules of hooks. As we explained earlier in this article, React uses the order of the hooks to know what value to store and pass to our code.
  • In the arrow function of the useEffect hook, we read our previously stored value. If it is true, then we are still in the initial rendering. So we just set it to false so the next time we arrive on the same line of code, our value correctly indicates us that we are not in the initial rendering anymore and we call our function.
  • We let the second argument to useEffect undefined, that way we indicate to React that nothing should prevent the hook from triggering.

3.4. From shouldComponentUpdate to memo and useMemo

shouldComponentUpdate will not be as straight forward as the previous ones. With React 16.7 you can either:

  • Use memo to memoize a component in regards to its props. Just like React.PureComponent would.
  • Use useMemo to memoize a component’s children.

3.5. From getDerivedStateFromProps to… render

Since your state is now located directly in your render function (since your component is the render function), there is no need for a hook to update a state.

3.6. From componentDidCatch to… nothing

Sadly, at this point there is no equivalent to componentDidCatch, so the only solution is to make a classic React.Component.

参考:

Making Sense of React Hooks: https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889 From React.Component to hooks: https://medium.com/@dispix/from-react-component-to-hooks-b50241334365 React Hooks: What's going to happen to my tests?: https://kentcdodds.com/blog/react-hooks-whats-going-to-happen-to-my-tests React Hooks: What's going to happen to render props?: https://kentcdodds.com/blog/react-hooks-whats-going-to-happen-to-render-props State Management with React Hooks — No Redux or Context API: https://medium.com/javascript-in-plain-english/state-management-with-react-hooks-no-redux-or-context-api-8b3035ceecf8 How to fetch data with React Hooks?: https://www.robinwieruch.de/react-hooks-fetch-data Primer on React Hooks: https://testdriven.io/blog/react-hooks-primer/ React Hooks - A deeper dive featuring useContext and useReducer: https://testdriven.io/blog/react-hooks-advanced/ Using Custom React Hooks to Simplify Forms: https://upmostly.com/tutorials/using-custom-react-hooks-simplify-forms The Guide to Learning React Hooks: https://www.telerik.com/kendo-react-ui/react-hooks-guide/ React by Example: Hooks https://reactbyexample.github.io/hooks/


本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-02-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WebJ2EE 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档