{"id":351396,"date":"2024-05-20T12:47:15","date_gmt":"2024-05-20T12:47:15","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=351396"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=351396","title":{"rendered":"<span>React Custom Hook: useStateWithHistory<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>In this article series, we embark on a journey through the realm of custom React hooks, discovering their immense potential for elevating your development projects. Our focus today is on the &#171;useStateWithHistory&#187; hook, one of the many carefully crafted hooks available in the collection of React custom hooks.<\/p>\n<p>Github:\u00a0<a href=\"https:\/\/github.com\/sergeyleschev\/react-custom-hooks\" rel=\"noopener noreferrer nofollow\"><u>https:\/\/github.com\/sergeyleschev\/react-custom-hooks<\/u><\/a><\/p>\n<pre><code class=\"javascript\">import { useCallback, useRef, useState } from \"react\"  export default function useStateWithHistory(     defaultValue,     { capacity = 10 } = {} ) {     const [value, setValue] = useState(defaultValue)     const historyRef = useRef([value])     const pointerRef = useRef(0)     const set = useCallback(         v => {             const resolvedValue = typeof v === \"function\" ? v(value) : v             if (historyRef.current[pointerRef.current] !== resolvedValue) {                 if (pointerRef.current &lt; historyRef.current.length - 1) {                     historyRef.current.splice(pointerRef.current + 1)                 }                 historyRef.current.push(resolvedValue)                 while (historyRef.current.length > capacity) {                     historyRef.current.shift()                 }                 pointerRef.current = historyRef.current.length - 1             }             setValue(resolvedValue)         },         [capacity, value]     )     const back = useCallback(() => {         if (pointerRef.current &lt;= 0) return         pointerRef.current--         setValue(historyRef.current[pointerRef.current])     }, [])     const forward = useCallback(() => {         if (pointerRef.current >= historyRef.current.length - 1) return         pointerRef.current++         setValue(historyRef.current[pointerRef.current])     }, [])     const go = useCallback(index => {         if (index &lt; 0 || index > historyRef.current.length - 1) return         pointerRef.current = index         setValue(historyRef.current[pointerRef.current])     }, [])     return [         value,         set,         {             history: historyRef.current,             pointer: pointerRef.current,             back,             forward,             go,         },     ] }<\/code><\/pre>\n<p>Advantages of <strong>useStateWithHistory<\/strong>:<\/p>\n<ol>\n<li>\n<p>Automatic history tracking: useStateWithHistory automatically keeps track of the values you set, allowing you to access the complete history whenever you need it.<\/p>\n<\/li>\n<li>\n<p>Efficient memory usage: The hook utilizes a capacity parameter, ensuring that the history doesn&#8217;t grow indefinitely. You can define the maximum number of historical values to keep, preventing excessive memory consumption.<\/p>\n<\/li>\n<li>\n<p>Time-travel functionality: With back(), forward(), and go() functions, you can seamlessly navigate through the recorded history. Travel back and forth between previous states or jump directly to a specific index, enabling powerful undo\/redo or step-by-step functionality.<\/p>\n<\/li>\n<\/ol>\n<p>Where to use <u>useStateWithHistory<\/u>:<\/p>\n<ol>\n<li>\n<p>Form management: Simplify the process of handling form inputs by providing an easy way to track changes, revert to previous values, or redo modifications.<\/p>\n<\/li>\n<li>\n<p>Undo\/Redo functionality: Implement undo\/redo functionality in your application with ease. Track state changes and allow users to navigate back and forth through their actions effortlessly.<\/p>\n<\/li>\n<li>\n<p>Step-by-step navigation: Use useStateWithHistory to build interactive guides or tutorials where users can navigate between different steps while preserving their progress.<\/p>\n<\/li>\n<\/ol>\n<pre><code class=\"javascript\">import { useState } from \"react\" import useStateWithHistory from \".\/useStateWithHistory\"  export default function StateWithHistoryComponent() {     const [count, setCount, { history, pointer, back, forward, go }] =         useStateWithHistory(1)     const [name, setName] = useState(\"Sergey\")     return (         &lt;div>             &lt;div>{count}&lt;\/div>             &lt;div>{history.join(\", \")}&lt;\/div>             &lt;div>Pointer - {pointer}&lt;\/div>             &lt;div>{name}&lt;\/div>             &lt;button onClick={() => setCount(currentCount => currentCount * 2)}>                 Double             &lt;\/button>             &lt;button onClick={() => setCount(currentCount => currentCount + 1)}>                 Increment             &lt;\/button>             &lt;button onClick={back}>Back&lt;\/button>             &lt;button onClick={forward}>Forward&lt;\/button>             &lt;button onClick={() => go(2)}>Go To Index 2&lt;\/button>             &lt;button onClick={() => setName(\"John\")}>Change Name&lt;\/button>         &lt;\/div>     ) }<\/code><\/pre>\n<p>Throughout this article series, we focused on one of the gems from the collection of React custom hooks \u2013 &#171;<em>useStateWithHistory<\/em>&#171;. This hook, sourced from the &#171;react-custom-hooks&#187; repository, revolutionizes how we work in our React applications. <\/p>\n<p>Full Version | React Custom Hooks: <a href=\"https:\/\/habr.com\/en\/articles\/746760\/\" rel=\"noopener noreferrer nofollow\">https:\/\/habr.com\/en\/articles\/746760\/<\/a><\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/752826\/\"> https:\/\/habr.com\/ru\/articles\/752826\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>In this article series, we embark on a journey through the realm of custom React hooks, discovering their immense potential for elevating your development projects. Our focus today is on the &#171;useStateWithHistory&#187; hook, one of the many carefully crafted hooks available in the collection of React custom hooks.<\/p>\n<p>Github:\u00a0<a href=\"https:\/\/github.com\/sergeyleschev\/react-custom-hooks\" rel=\"noopener noreferrer nofollow\"><u>https:\/\/github.com\/sergeyleschev\/react-custom-hooks<\/u><\/a><\/p>\n<pre><code class=\"javascript\">import { useCallback, useRef, useState } from \"react\"  export default function useStateWithHistory(     defaultValue,     { capacity = 10 } = {} ) {     const [value, setValue] = useState(defaultValue)     const historyRef = useRef([value])     const pointerRef = useRef(0)     const set = useCallback(         v => {             const resolvedValue = typeof v === \"function\" ? v(value) : v             if (historyRef.current[pointerRef.current] !== resolvedValue) {                 if (pointerRef.current &lt; historyRef.current.length - 1) {                     historyRef.current.splice(pointerRef.current + 1)                 }                 historyRef.current.push(resolvedValue)                 while (historyRef.current.length > capacity) {                     historyRef.current.shift()                 }                 pointerRef.current = historyRef.current.length - 1             }             setValue(resolvedValue)         },         [capacity, value]     )     const back = useCallback(() => {         if (pointerRef.current &lt;= 0) return         pointerRef.current--         setValue(historyRef.current[pointerRef.current])     }, [])     const forward = useCallback(() => {         if (pointerRef.current >= historyRef.current.length - 1) return         pointerRef.current++         setValue(historyRef.current[pointerRef.current])     }, [])     const go = useCallback(index => {         if (index &lt; 0 || index > historyRef.current.length - 1) return         pointerRef.current = index         setValue(historyRef.current[pointerRef.current])     }, [])     return [         value,         set,         {             history: historyRef.current,             pointer: pointerRef.current,             back,             forward,             go,         },     ] }<\/code><\/pre>\n<p>Advantages of <strong>useStateWithHistory<\/strong>:<\/p>\n<ol>\n<li>\n<p>Automatic history tracking: useStateWithHistory automatically keeps track of the values you set, allowing you to access the complete history whenever you need it.<\/p>\n<\/li>\n<li>\n<p>Efficient memory usage: The hook utilizes a capacity parameter, ensuring that the history doesn&#8217;t grow indefinitely. You can define the maximum number of historical values to keep, preventing excessive memory consumption.<\/p>\n<\/li>\n<li>\n<p>Time-travel functionality: With back(), forward(), and go() functions, you can seamlessly navigate through the recorded history. Travel back and forth between previous states or jump directly to a specific index, enabling powerful undo\/redo or step-by-step functionality.<\/p>\n<\/li>\n<\/ol>\n<p>Where to use <u>useStateWithHistory<\/u>:<\/p>\n<ol>\n<li>\n<p>Form management: Simplify the process of handling form inputs by providing an easy way to track changes, revert to previous values, or redo modifications.<\/p>\n<\/li>\n<li>\n<p>Undo\/Redo functionality: Implement undo\/redo functionality in your application with ease. Track state changes and allow users to navigate back and forth through their actions effortlessly.<\/p>\n<\/li>\n<li>\n<p>Step-by-step navigation: Use useStateWithHistory to build interactive guides or tutorials where users can navigate between different steps while preserving their progress.<\/p>\n<\/li>\n<\/ol>\n<pre><code class=\"javascript\">import { useState } from \"react\" import useStateWithHistory from \".\/useStateWithHistory\"  export default function StateWithHistoryComponent() {     const [count, setCount, { history, pointer, back, forward, go }] =         useStateWithHistory(1)     const [name, setName] = useState(\"Sergey\")     return (         &lt;div>             &lt;div>{count}&lt;\/div>             &lt;div>{history.join(\", \")}&lt;\/div>             &lt;div>Pointer - {pointer}&lt;\/div>             &lt;div>{name}&lt;\/div>             &lt;button onClick={() => setCount(currentCount => currentCount * 2)}>                 Double             &lt;\/button>             &lt;button onClick={() => setCount(currentCount => currentCount + 1)}>                 Increment             &lt;\/button>             &lt;button onClick={back}>Back&lt;\/button>             &lt;button onClick={forward}>Forward&lt;\/button>             &lt;button onClick={() => go(2)}>Go To Index 2&lt;\/button>             &lt;button onClick={() => setName(\"John\")}>Change Name&lt;\/button>         &lt;\/div>     ) }<\/code><\/pre>\n<p>Throughout this article series, we focused on one of the gems from the collection of React custom hooks \u2013 &#171;<em>useStateWithHistory<\/em>&#171;. This hook, sourced from the &#171;react-custom-hooks&#187; repository, revolutionizes how we work in our React applications. <\/p>\n<p>Full Version | React Custom Hooks: <a href=\"https:\/\/habr.com\/en\/articles\/746760\/\" rel=\"noopener noreferrer nofollow\">https:\/\/habr.com\/en\/articles\/746760\/<\/a><\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/752826\/\"> https:\/\/habr.com\/ru\/articles\/752826\/<\/a><br \/><\/br><\/br><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-351396","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/351396","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=351396"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/351396\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=351396"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=351396"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=351396"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}