{"resource":{"author":{"id":"h2so9H8jPMmgUKGghoNl","name":"Yomesh Gupta","username":"yomeshgupta"},"content":{"link":"https://devtools.tech/intro-to-react-hooks/","difficulty":2,"domain":2,"type":1,"isInternal":true,"body":"React is one of the most popular and growing UI libraries today. With every new release, there are new features and deprecations along with it. Let's talk about one of the newest features of React, known as Hooks.\n\n## Why React Hooks?\n\nHooks are a new addition to React 16.8 to provide state management and side-effects in function components.\n\n1. Earlier, only class components were used for local state management and lifecycle methods. These lifecycle methods have been essential for introducing side-effects, such as data fetching, listeners and many more. This led to a lot of refactoring from `functional stateless components` to `stateful class components` whenever a functional component needed to use state or lifecycle methods. With Hooks, we can use features like _state_ and _effects_ without actually any component transformation. We will talk more about `useState` and `useEffect` while building the app later in the post.\n\n```js\nimport React, { useState, useEffect } from \"react\";\n\nfunction Counter() {\n  // Using state in a functional component\n  const [count, setCount] = useState(0);\n\n  // Using effects in a functional component\n  useEffect(() => {\n    document.title = `Counter: ${count}`;\n  }, [count]);\n\n  render(\n    <div>\n      <p>You have clicked {count} times</p>\n      <button onClick={() => setCount(count + 1)}>Increment</button>\n    </div>\n  );\n}\n```\n\n2. Reusable stateful behaviour between React Components is a bit tricky. Though, it can be done using patterns like [render props](https://reactjs.org/docs/render-props.html) and [higher order components](https://reactjs.org/docs/higher-order-components.html). Using such patterns, components have to be restructured which makes code harder to understand and maintain. With Hooks, stateful logic can be extracted from the components into their custom hooks which allows them to be tested independently and can be reused.\n\n## Let's start building!\n\nWe are going to build a simple Todo App in this blog post. Demo for the same can be seen [here](https://codesandbox.io/s/71j9q45y11) and Github repo [here](https://github.com/yomeshgupta/react-hooks-todo-app).\n\n```js\nimport React, { useState } from 'react';\n\nfunction Todos() {\n  // Creating a todos state variable\n  const [todos, setTodos] = useState({\n    1552406885681: {\n      todo: 'Complete this blog post',\n      isComplete: false\n    }\n  });\n\n  // Rendering the todos\n  return <div className=\"wrapper\">\n    <ul id=\"todos\">\n      {Object.entries(todos).map(([key, value]) => <li key={key}>{value.todo}</li>);}\n    </ul>\n  </div>;\n}\n```\n\n### Defining state with useState()\n\nAs mentioned earlier, now we can do state management in functional components and to do so React provides us with a hook called `useState`.\n\n1. It takes an initial state. Unlike the class component's state, useState's initial state need not be an object. It can be a string, boolean, object or any other possible value in JavaScript.\n\n```js\nconst [count, setCount] = useState(0); // number\nconst [name, setName] = useState(\"Yomesh\"); // string\nconst [fetched, setFetched] = useState(false); // boolean\nconst [todos, setTodos] = useState({}); // object\n```\n\n2. It declares a \"state variable\" whose value persists between the function calls. It provides the same capabilities as `this.state`.\n3. It returns a pair of values: the current state and a function that updates it. We get these return values via array destructing `const [todos, setTodos] = useState({});`\n\nIn the above code example, we created a state variable called `todos` with default value as our current todos.\n\n### Fetching Todos\n\nEarlier, we have provided default values to our todos but what if we have to fetch those todos from a remote API? In such cases, we need something like `componentDidMount` for data fetching like we used to do in class components.\n\nReact provides us with a hook called `useEffect` which can be used directly into a component and provides a way to mimic these lifecycle methods and go beyond that. When we talk about effects, we are referring to things like data fetching, updates to the DOM, event listeners and likes. Let's see this in action step by step.\n\n```js\nimport React, { useState, useEffect } from \"react\";\n\nconst TODOS = {\n  1552406885681: {\n    todo: \"Complete this blog post\",\n    isComplete: false,\n  },\n};\n\nfunction Todos() {\n  // Creating a todos state variable\n  const [todos, setTodos] = useState({});\n\n  // Setting up an effect\n  useEffect(function () {\n    // fetch(REMOTE_URL).then(response => setTodos(response));\n\n    // mocking API call\n    return new Promise((resolve, reject) => {\n      setTimeout(() => resolve(TODOS), 2000);\n    }).then((response) => {\n      // Updating state variable\n      setTodos(response);\n    });\n  });\n\n  // Rendering the todos\n  return (\n    <div className=\"wrapper\">\n      <ul id=\"todos\">\n        {Object.keys(todos).map((key) => {\n          const value = todos[key];\n          return <li key={key}>{value.todo}</li>;\n        })}\n      </ul>\n    </div>\n  );\n}\n```\n\n`useEffect` always run after the render. So, they are non-blocking in nature. Consider them like giving React an instruction and executing it after the DOM has rendered. It takes two arguments: a function that will be executed after the render and a dependency array (More on this below).\n\nIn the above code snippet:\n\n1. We are setting up an effect that is used to fetch data from an API (mocking). So, initially, our todos will be empty and we won't be seeing anything. After the initial render, `useEffect` will execute and data is being fetched.\n\n2. An effect hook should return nothing or a cleanup function. That's why you may see the following error in your developer console log - `Warning: An Effect function must not return anything besides a function, which is used for clean-up. It looks like you wrote useEffect(async () => ...) or returned a Promise. Instead, you may write an async function separately and then call it from inside the effect`. We can fix this via\n\n```js\n  ...\n  // Setting up an effect\n  useEffect(function() {\n    function fetchData() {\n      // fetch(REMOTE_URL).then(response => setTodos(response));\n\n      // mocking API call\n      new Promise((resolve, reject) => {\n        setTimeout(() => resolve(TODOS), 2000);\n      }).then(response => {\n        // Updating state variable\n        setTodos(response);\n      });\n    }\n\n    fetchData();\n  });\n  ...\n}\n```\n\n3. This is all good and fine but if you execute the code so far, you will see that `fetchData` will be called after every render as `useEffect` executes after every renders too! Check out this [sandbox](https://codesandbox.io/s/xpjrkj2wxo) to see what I mean.\n\nTo avoid this infinite loop and for optimisation, `useEffect` takes a second argument which is called a `dependency array`. In this array, we can mention all the variables on whose value change -- execution of `useEffect` depends. Suppose, we have a use-case where we need to display a list of items, fetched from a remote API, based on an input query. In this case, the input query would be part of the dependency array. Check out this [sandbox](https://codesandbox.io/s/l0n6qn3x7) for a live example.\n\nBut in our app, we only need to fetch data once after the initial load. We can do that by providing an empty array as the second argument to `useEffect`. By doing so, our effect will only run once after the initial render, acting just like `componentDidMount` here.\n\n```js\n...\nuseEffect(function() {\n  {\n    /* some processing */\n  }\n}, []); // acts like componentDidMount\n...\n```\n\n4. Now, the data fetching part is done. Focus on the code below\n\n```js\n.then(response => setTodos(response));\n```\n\nAs we discussed earlier, `const [todos, setTodos] = useState({})` provides us two return values. Second value is a function which allows us to update the state variable. Here, it is same as doing `this.setState({ todos: response })`\nin a class component.\n\n### Adding Loader and Empty State\n\nWe will add `Loader` and `Empty` components. Initially, there would be no todos so `Loader` will be displayed and if the fetch returns no result then the `Empty` state\nwill be displayed.\n\n```js{2-9,11,13,29-33}\n...\nfunction Loader() {\n  return <div id=\"loader\">Loading...</div>;\n}\n\nfunction Empty() {\n  return <div id=\"empty\">No todos found...</div>;\n}\n\nfunction Todos() {\n  const [fetched, setFetched] = useState(false);\n  const [todos, setTodos] = useState({});\n  const keys = Object.keys(todos);\n\n  useEffect(function() {\n    function fetchData() {\n      new Promise((resolve, reject) => {\n        setTimeout(() => resolve(TODOS), 2000);\n      }).then(response => {\n        setFetched(true);\n        setTodos(response);\n      });\n    }\n\n    fetchData();\n  }, []);\n\n  function renderContent() {\n    if (!fetched) {\n      return <Loader />;\n    } else if (!keys.length) {\n      return <Empty />;\n    }\n    return (\n      <ul id=\"todos\">\n        {keys.map(key => {\n          const value = todos[key];\n          return <li key={key}>{value.todo}</li>;\n        })}\n      </ul>\n    );\n  }\n\n  return <div className=\"wrapper\">{renderContent()}</div>;\n}\n```\n\n### Refactoring...\n\nSo far so good but we can take it up a notch. People coming from the realm of `Redux` will enjoy it.\n\n#### Welcome useReducer!\n\nIt is an alternative to `useState`. It accepts three arguments -- `reducer`, `initialState`, `init function`, and returns current `state` and `dispatch` function to update that state.\n\nAs per React documentation,\n\n> useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one.\n\n```js\nconst [state, dispatch] = useReducer(reducer, initialState, init);\n```\n\nLet's refactor some of our code now.\n\n```js{9-22,25,34-37}\nimport React, { useEffect, useReducer } from 'react';\n\nconst TODOS = {\n  1552406885681: {\n    todo: 'Complete this blog post',\n    isComplete: false\n  }\n};\n\nconst initialState = {\n  fetched: false,\n  todos: {}\n};\n\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'REPLACE_TODOS':\n      return { ...state, fetched: true, todos: action.payload };\n    default:\n      return state;\n  }\n}\n...\nfunction Todos() {\n  const [state, dispatch] = useReducer(reducer, initialState);\n  const { fetched, todos } = state;\n  const keys = Object.keys(todos);\n\n  useEffect(function() {\n    function fetchData() {\n      new Promise((resolve, reject) => {\n        setTimeout(() => resolve(TODOS), 2000);\n      }).then(response => {\n        dispatch({\n          type: 'REPLACE_TODOS',\n          payload: response\n        });\n      });\n    }\n\n    fetchData();\n  }, []);\n  ...\n}\n```\n\nWe can use `dispatch` deep inside the component hierarchy and update our state, just like good old plain `Redux Actions`!\n\n### Let's Save, Complete and Delete\n\nIn this section, we are going to add the following functionalities\n\n#### Saving a new todo\n\nHere, we declare a new state variable `task` and will add a form. We are going to capture the new todo in the state variable and add it to the list via dispatching a new action when the form submits.\n\n```js{2,7-14,21-29,35-38}\n...\nconst [task, setTask] = useState('');\n\nfunction reducer(state, action) {\n  switch (action.type) {\n    ...\n    case 'ADD_TODO':\n      return {\n        ...state,\n        todos: {\n          ...state.todos,\n          ...action.payload\n        }\n      }\n    ...\n  }\n}\n...\nfunction saveHandler(e) {\n  e.preventDefault();\n  dispatch({\n    type: 'ADD_TODO',\n    payload: {\n      [+new Date()]: {\n        todo: task,\n        isComplete: false\n      }\n    }\n  });\n  setTask('');\n}\n\nreturn (\n  <div className=\"wrapper\">\n    <form method=\"POST\" onSubmit={saveHandler}>\n      <input type=\"text\" onChange={e => setTask(e.target.value)} value={task} placeholder=\"What needs to be done?\" />\n      <input type=\"submit\" value=\"Add\" />\n    </form>\n...\n```\n\n#### Marking a todo as complete\n\nNow, we are going to add some controls to the to-do list. I have highlighted the added code. As you can see, we have added a `Check FontAwesomeIcon`. Upon clicking the check control, action is dispatch which updates our current state and sets the `isComplete` flag to `true` for that particular todo.\n\nYou can always refer to this [sandbox](https://codesandbox.io/s/71j9q45y11) for a live example to visualize it better.\n\n```js{5-15,19-32,45-56}\n...\nfunction reducer(state, action) {\n  switch (action.type) {\n    ...\n    case \"COMPLETE_TODO\":\n      return {\n        ...state,\n        todos: {\n          ...state.todos,\n          [action.payload.id]: {\n            ...state.todos[action.payload.id],\n            isComplete: true\n          }\n        }\n      };\n    ...\n  }\n}\nfunction controlHandler(id, operation) {\n  switch (operation) {\n    case \"complete\":\n      dispatch({\n        type: \"COMPLETE_TODO\",\n        payload: {\n          id\n        }\n      });\n      break;\n    default:\n      console.log(\"This is odd.\");\n  }\n}\n...\n  function renderContent() {\n    ...\n    return (\n      <ul id=\"todos\">\n        {keys.map(key => {\n          ...\n          return (\n            <li key={key}>\n              <p className={isComplete ? \"complete\" : \"\"}>\n                {todo}\n              </p>\n              <div class=\"controls\">\n                {!isComplete ? (\n                  <FontAwesomeIcon\n                    icon=\"check\"\n                    title=\"Mark as Complete\"\n                    className=\"control-icon\"\n                    onClick={() =>\n                      controlHandler(key, \"complete\")\n                    }\n                  />\n                ) : null}\n              </div>\n            </li>\n          );\n        })}\n      </ul>\n    );\n  }\n...\n```\n\n#### Removing it once it has served its purpose\n\nJust like complete functionality, we will add a delete icon with a click handler that filters the todos and update our state. Below is the complete working code of our app. I have highlighted the part added for removing a to-do item.\n\nYou can always refer to this [sandbox](https://codesandbox.io/s/71j9q45y11) for a live example to visualize it better.\n\n```js{35-37,117-125,154-159}\n/*\n  Author: Yomesh Gupta (https://www.twitter.com/yomeshgupta)\n*/\n\nimport React, { useEffect, useState, useReducer } from 'react';\nimport ReactDOM from 'react-dom';\nimport { library } from '@fortawesome/fontawesome-svg-core';\nimport { faCheck, faTrash } from '@fortawesome/free-solid-svg-icons';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\n\nimport './styles.css';\n\nlibrary.add(faCheck, faTrash);\n\nconst initialState = {\n  fetched: false,\n  todos: {}\n};\n\nconst TODOS = {\n  1552406885681: {\n    todo: 'Complete this blog post',\n    isComplete: false\n  },\n  1552406885682: {\n    todo: 'Add everything to this blog post',\n    isComplete: false\n  }\n};\n\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'REPLACE_TODOS':\n      return { ...state, fetched: true, todos: action.payload };\n    case 'UPDATE_TODOS': {\n      return { ...state, todos: action.payload };\n    }\n    case 'ADD_TODO':\n      return {\n        ...state,\n        todos: {\n          ...state.todos,\n          ...action.payload\n        }\n      };\n    case 'COMPLETE_TODO':\n      return {\n        ...state,\n        todos: {\n          ...state.todos,\n          [action.payload.id]: {\n            ...state.todos[action.payload.id],\n            isComplete: true\n          }\n        }\n      };\n    default:\n      return state;\n  }\n}\n\nfunction Loader() {\n  return <div id=\"loader\">Loading...</div>;\n}\n\nfunction Empty() {\n  return <div id=\"empty\">Seems kind of empty here...</div>;\n}\n\nfunction Todos() {\n  const [task, setTask] = useState('');\n  const [state, dispatch] = useReducer(reducer, initialState);\n  const { fetched, todos } = state;\n  const keys = Object.keys(todos);\n\n  // Setting up an effect\n  useEffect(function() {\n    function fetchData() {\n      new Promise((resolve, reject) => {\n        // mocking API call\n        setTimeout(() => resolve(TODOS), 2000);\n      }).then(response => {\n        // Updating state variable\n        dispatch({\n          type: 'REPLACE_TODOS',\n          payload: response\n        });\n      });\n    }\n    fetchData();\n  }, []);\n\n  function saveHandler(e) {\n    e.preventDefault();\n    dispatch({\n      type: 'ADD_TODO',\n      payload: {\n        [+new Date()]: {\n          todo: task,\n          isComplete: false\n        }\n      }\n    });\n    setTask('');\n  }\n\n  function controlHandler(id, operation) {\n    switch (operation) {\n      case 'complete':\n        dispatch({\n          type: 'COMPLETE_TODO',\n          payload: {\n            id\n          }\n        });\n        break;\n      case 'delete': {\n        const clonedTodos = { ...todos };\n        delete clonedTodos[id];\n        dispatch({\n          type: 'UPDATE_TODOS',\n          payload: clonedTodos\n        });\n        break;\n      }\n      default:\n        console.log('This is odd.');\n    }\n  }\n\n  function renderContent() {\n    if (!fetched) {\n      return <Loader />;\n    } else if (!keys.length) {\n      return <Empty />;\n    }\n    return (\n      <ul id=\"todos\">\n        {keys.map(key => {\n          const value = todos[key];\n          const { isComplete, todo } = value;\n          return (\n            <li key={key}>\n              <p className={isComplete ? 'complete' : ''}>{todo}</p>\n              <div class=\"controls\">\n                {!isComplete ? (\n                  <FontAwesomeIcon\n                    icon=\"check\"\n                    title=\"Mark as Complete\"\n                    className=\"control-icon\"\n                    onClick={() => controlHandler(key, 'complete')}\n                  />\n                ) : null}\n                <FontAwesomeIcon\n                  icon=\"trash\"\n                  title=\"Delete Todo\"\n                  className=\"control-icon\"\n                  onClick={() => controlHandler(key, 'delete')}\n                />\n              </div>\n            </li>\n          );\n        })}\n      </ul>\n    );\n  }\n\n  return (\n    <div className=\"wrapper\">\n      <form method=\"#\" onSubmit={saveHandler}>\n        <input\n          type=\"text\"\n          onChange={e => setTask(e.target.value)}\n          value={task}\n          placeholder=\"What needs to be done?\"\n        />\n        <input type=\"submit\" value=\"Add\" title=\"Add Todo\" />\n      </form>\n      {renderContent()}\n    </div>\n  );\n}\nconst rootElement = document.getElementById('root');\nReactDOM.render(<Todos />, rootElement);\n```\n\nFinally, our app is complete! Phew! However, we can go ahead and implement more functionalities like error handling, more controls, due date etc!\nThere are more hooks provided by React itself and we can even write our custom hooks! Let's keep that for part two of this blog post.\n\nQuick Links: [Working Sandbox](https://codesandbox.io/s/71j9q45y11) | [Github Repo](https://github.com/yomeshgupta/react-hooks-todo-app)\n\nA powerful and more advanced version of this Todo App, along with many more features, is shipped with a super awesome chrome extension called [Backdrops](https://chrome.google.com/webstore/detail/backdrops/beanogjmmfajlfkfmlchaoamcoelddjf). You should check it out.\n\n> Backdrops is an amazing Chrome extension that turns your boring chrome new tab into something more delightful and useful. Check it out [here](https://chrome.google.com/webstore/detail/backdrops/beanogjmmfajlfkfmlchaoamcoelddjf).\n","languages":[],"editorConfig":{}},"stats":{"views":10822,"used":0,"likes":0},"description":"","published":true,"isActive":true,"tags":["reactjs","frontend","javascript","hooks","ui","react hooks","todo app"],"slug":"intro-to-react-hooks-or-todo-app---rid---ni3m5eFHiWcTkScUkhQw","isPremium":false,"categories":[],"requires":[],"_id":"5f1dd580cbec5f7ffc0c2fa9","title":"Intro to React Hooks | Todo App","resourceId":"ni3m5eFHiWcTkScUkhQw","createdAt":1595790720840,"modifiedAt":1643031242631},"currentUser":null,"isOwner":false,"recommendations":{"questions":[{"_id":"693e85d77a8f644500329fb6","content":{"languages":["react"],"difficulty":2},"tags":["javascript","frontend","ui","ux","devtools tech","frontend interview challenge","coding question"],"slug":"cursor-tracker---qid---ATU3sJ5FPYzRkAEweNuS","title":"Cursor Tracker","questionId":"ATU3sJ5FPYzRkAEweNuS"},{"_id":"648b384cd12c1e2402053b07","content":{"languages":["react","html"],"difficulty":1},"tags":["javascript","frontend","react","ui","html","css","frontend coding challenge","razorpay","ui challenges","coding","codedamn","egghead","frontend masters","price calculator"],"slug":"build-a-simple-price-calculator-or-frontend-coding-challenge-or-razorpay-interview-question---qid---2lGvliAvIzreHeVSRfGD","title":"Build a Simple Price Calculator | Frontend Coding Challenge | Razorpay Interview Question","questionId":"2lGvliAvIzreHeVSRfGD"},{"_id":"636d3c2f20622762d08d016e","content":{"languages":["javascript"],"difficulty":1},"tags":["javascript","frontend","coding","devtools tech","interview questions","interview preparation","mcq","programming paradigm","tooling","mdn","js paradigm","programming questions","switch case","comparison"],"slug":"how-the-comparison-happens-with-switch-cases-in-javascript---qid---Dq9nqwLRz1czCqtsFrm0","title":"How the comparison happens with switch cases in JavaScript?","questionId":"Dq9nqwLRz1czCqtsFrm0"},{"_id":"6a2823d12d3b6d374c214f3a","content":{"languages":["javascript","typescript"],"difficulty":1},"tags":["javascript","ui","ux","devtools tech","coding","frontend coding challenges","currying","goibibo"],"slug":"dynamic-currying-sum---qid---h8zqyBPk01SgCnuSM7zh","title":"Dynamic Currying Sum","questionId":"h8zqyBPk01SgCnuSM7zh"},{"_id":"680ca850554ede0bfb0f5ce4","content":{"languages":["html","react"],"difficulty":3},"tags":["javascript","ui","ux","devtools tech","coding","frontend","flipkart","interview questions","frontend interview questions","google"],"slug":"straws-on-the-board---qid---2zxuCdkBmZTdHCH0CKBo","title":"Straws on the Board","questionId":"2zxuCdkBmZTdHCH0CKBo"}],"resources":[{"_id":"620406cce12bce537863eaf0","content":{"difficulty":4,"domain":1,"type":1,"isInternal":true},"tags":["javascript","remix run","social login","login with google","express","session","encryption","js fundamentals"],"slug":"add-social-authentication-to-remix-run-project-or-express-server---rid---jVwo6agJvN2oX4IegYgz","title":"Add Social Authentication to Remix Run Project | Express Server","resourceId":"jVwo6agJvN2oX4IegYgz"},{"_id":"5fc7215a6d3cda64e470c415","content":{"difficulty":4,"domain":1,"type":5},"tags":["linkedin","jobs","skills","career","indemand","devtools"],"slug":"move-close-to-your-dream-job-find-out-top-skills-in-the-market---rid---layhD7GHUKsulhIM25yM","title":"Move close to your dream job. Find out top skills in the market.","resourceId":"layhD7GHUKsulhIM25yM"},{"_id":"62d3b928d3473370ac372879","content":{"difficulty":4,"domain":2,"type":2,"isInternal":false},"tags":["javascript","promises","async programming","advanced javascript","interview question","frontend","coding"],"slug":"javascript-promises-fundamentals-every-engineer-should-know-or-part-2-or-event-loop-or-microtasks---rid---oBrzK6Mt7HNAnKBT3ogC","title":"JavaScript Promises Fundamentals Every Engineer Should Know | Part 2 | Event Loop | MicroTasks","resourceId":"oBrzK6Mt7HNAnKBT3ogC"},{"_id":"5f228218cbec5f7ffc0c2fc1","content":{"difficulty":2,"domain":2,"type":1},"tags":["frontend","css","styling","web","html","frontend fundamentals"],"slug":"what-does-100percent-mean-in-css---rid---S8XUNf7uoPo269MkjHvT","title":"What does 100% mean in CSS?","resourceId":"S8XUNf7uoPo269MkjHvT"},{"_id":"6a12e5104b3e6d922c54a0cf","content":{"difficulty":1,"domain":1,"type":1,"isInternal":true,"languages":[]},"tags":["javascript","ui","ux","devtools tech","coding","frontend"," web performance"],"slug":"reducing-image-bytes---rid---eoeW8SDZdwwNWBtFqfOh","title":"Reducing Image Bytes","resourceId":"eoeW8SDZdwwNWBtFqfOh"}]}}