React hooks and the confusions around it
A Photo from unsplash.com

React hooks and the confusions around it

Its React 16.8. Great!

We started using hooks in our React projects. It seems very simple to start with. Now, we can write all our components with just functions. There is no need for class based components now. and managing React state locally (via useState) and also globally (via contextAPI and useReducer) has become easier.

whenever we need to preserve some state in a functional component, we can just use 'useState' hook like so

  const [showLoader, setShowLoader] = useState(false)

This gives us a state variable and a function to set value on that state variable.

If we have a side effect when the component mounts for the first time or whenever any of the dependencies change, we use 'useEffect' hook. It is perfect for making API calls. e.g.,

  useEffect(() => {
      const fetchData = async ()=> {
        try{
          const response = await Apis.listCharacters(state.pageNumber)
        }catch(error)
        {
          console.log(error)
          setRedirectToError(true)
        }
      }
      fetchData()
    }
  },[state.pageNumber, dispatch])  

So, where is the confusion?

I didn't realise this until I hammered hooks hard enough. Even in a very small and tiny project, we often make quite a few API calls and manage logic such as API failed, loader hide/show, pagination etc. If the component also offers a searching logic then user can search with a new phrase any time and that needs to be factored in too on how the re-renders are handled. cutting long story short, a component ends up having several useEffects and several useStates. This will make the code very hard to read, bloated and not reusable at all.

How do we make such a code to read better?

Think 'Composition'.

Just like how React components follow composition design pattern and so can React hooks. This is where confusion was (at least for me). I never thought that React hooks can be composed in a similar way.

we can write our own hooks (aka custom hooks) which uses other hooks. just like a react component can use other components. We can make code more reusable and easy to read by writing hooks this way. its lifecycle doesn't get impacted as react guarantees to invoke the hook at right time depending on component rendering. and it doesn't matter if useEffect appears directly inside a functional component or inside your custom hook.

An example

I had these 3 useEffect functions in my component earlier.

  useEffect(()=>{
    if(state.results.length === 0)
    {
      setFireCall(true)
    }
  },[state.results.length])


  useEffect(() => {
    if(state.searchPhrase.length > 0 && searchDirty)
    {
      const id = setTimeout(()=>{
        reset()
        setFireCall(true)
      },1000)
      return () => {
        clearTimeout(id)
      }
    }
  }, [state.searchPhrase, searchDirty, reset])


  useEffect(() => {
    if(fireCall)
    {
      const fetchData = async ()=> {
        try{
          const response = await Apis.listCharacters(state.pageNumber,state.searchPhrase)
          dispatch({type:'SET_CHARACTERS', payload:response.results})
          dispatch({type:'SET_CURRENT_TOTAL', payload:response.offset + response.count})
          dispatch({type:'SET_OVERALL_TOTAL', payload: response.total})
          setFireCall(false)
          setSearchDirty(false)
        }catch(error)
        {
          console.log(error)
          setRedirectToError(true)
        }
      }
      fetchData()
    }
  },[fireCall, state.pageNumber, state.searchPhrase, dispatch])  


Using hooks composition pattern, I manage to write a custom hook which help the code to reduce to one line

  const [fetchNext, debouncedFetchData, cancelSearch, showLoader, redirectToError] = useFetchData()


useFetchData is a custom hook (it should start with the word - use) which in turn has several useState and useEffects inside it. It returns all required state variables and methods that a consuming component can use. The useEffects inside a custom hook will be invoked just how it would normally get invoked if they were directly inside a functional component.

Summary

  • Use composition pattern to write custom hooks to encapsulate the logic.
  • Do not mix data fetching, pagination or business logic code inside a component.
  • Do not even mix a code that does 'dispatch' calls to redux store inside a component.
  • Always, extract such code in a custom hook that returns a nice interface in the form of state variables and methods to the consuming component.

P.S.

Why it is called a hook ?

Because it offers a mechanism to tap into React's internals as in to hook into it.


要查看或添加评论,请登录

Dilip Agheda的更多文章

  • Writing code that tells you a story

    Writing code that tells you a story

    Uncle Bob mentioned in his book "Clean coder" about writing code that tells you a story. A story? What does it mean in…

  • React Internals

    React Internals

    Why component names begin with a capital letter? If you have been using React for sometime, you must have noticed that…

  • Avoid memory leak in your React.js apps

    Avoid memory leak in your React.js apps

    What is memory leak? In computer science, a memory leak is a type of resource leak that occurs when a computer program…

    1 条评论
  • How to be insanely good at building a test automation framework using Cypress

    How to be insanely good at building a test automation framework using Cypress

    Recently, few people have asked me how to learn cypress and where to go for resources. I thought instead of replying…

    3 条评论

社区洞察

其他会员也浏览了