[{"params":{"slug":"2023-11-13-making-the-most-of-tailwind-css"},"props":{"id":"2023-11-13-making-the-most-of-tailwind-css.md","slug":"2023-11-13-making-the-most-of-tailwind-css","body":"\n[TailwindCSS](https://tailwindcss.com) is a great library for easily composing styles. I have been\nusing Tailwind for a while and I absolutely love it. The bundle remains small by removing unused\nclasses, while redundant styles don't pile up almost at all, since all the classes end up global and\navailable to all. It's a very different way of writing styles and I am fond of it.\n\nWhile using Tailwind I have found several things that weren't immediately known to me from the\nstart, and which I think could benefit anyone using the library. Here is a collection of some of the\nbest tips I have learned.\n\n## Set your override colors\n\nIt may sound either dumb, or immediatlely obvious. But maybe I wasn't aware of the importance of\ncorrectly labeling your colors, shadesm and making sure they easily align with your project's design\nsystem.\n\n### Color objects\n\nFirst thing's first, to familiarize ourselves with the options, when we define colors, they can come\nas either objects, or single color strings. Example:\n\n```ts\nexport default {\n theme: {\n extend: {\n colors: {\n // single color\n blue: '#0000ff',\n // color with shades\n curious: {\n 50: '#f0f9ff',\n 100: '#e0f2fe',\n 200: '#bbe5fc',\n 300: '#7fd0fa',\n 400: '#3ab9f6',\n 500: '#11a1e6',\n 600: '#058ed9',\n 700: '#05669f',\n 800: '#095783',\n 900: '#0d496d',\n 950: '#092e48',\n },\n },\n },\n },\n}\n```\n\nNow that we know the basics, we can start talking strategy.\n\n### Color aliases\n\nWhat I usually do is always include the original colors, but also include some aliases, or\ncompletely new colors so that I can use them more easily. For example, by using `primary` and\n`secondary` colors as aliases to other colors, you can easily update your entire site's theme\nwithout going over the actual components.\n\nHere is an example of a color scheme using this method:\n\n```ts\n// get the original tailwind colors\nimport colors from 'tailwindcss/colors'\n\nexport default {\n theme: {\n extend: {\n colors: {\n ...colors,\n primary: colorScheme.indigo,\n secondary: colorScheme.purple,\n },\n },\n },\n}\n```\n\nThis way, we can use classes such as `text-primary-500`, `bg-secondary-900` and more, and easily be\nable to swap them out when needed.\n\n### The `DEFAULT` keyword\n\nAnother important tip about colors is to make use of the `DEFAULT` key in a given color\nconfiguration.\n\nSay we have the following color:\n\n```ts\nexport default {\n theme: {\n extend: {\n colors: {\n curious: {\n 50: '#f0f9ff',\n 100: '#e0f2fe',\n 200: '#bbe5fc',\n 300: '#7fd0fa',\n 400: '#3ab9f6',\n 500: '#11a1e6',\n 600: '#058ed9',\n 700: '#05669f',\n 800: '#095783',\n 900: '#0d496d',\n 950: '#092e48',\n },\n },\n },\n },\n}\n```\n\nWe are able to use classes such as `text-curious-100` to use the 100 shade. But what if we wanted a\nmain color where we don't have to specify the shade each time?\n\nIf we add a `DEFAULT` color, it will be the one used when there is no shade part given in the class.\n\nLet's add one:\n\n```ts\nconst curious = {\n // default:\n DEFAULT: '#058ed9',\n 50: '#f0f9ff',\n 100: '#e0f2fe',\n 200: '#bbe5fc',\n 300: '#7fd0fa',\n 400: '#3ab9f6',\n 500: '#11a1e6',\n 600: '#058ed9',\n 700: '#05669f',\n 800: '#095783',\n 900: '#0d496d',\n 950: '#092e48',\n}\n```\n\nNow, we can just use `text-curious` and `bg-curious` and we always get the primary version of the\ncolor.\n\n### Contrast colors\n\nAnother thing I like to do is add `fg` or `foreground` keys to my colors.\n\nThis makes it easy for me to always find a contrasting text color for any given color.\n\nFor example, if I have a primarily dark background color, and I want a color that is always visible\nagainst it, to add text or icons.\n\nI would do this (real example from this blog):\n\n```ts\nconst flamingo = {\n DEFAULT: '#fc5130',\n fg: '#ffffff',\n 50: '#fff2ed',\n 100: '#ffe1d5',\n 200: '#ffbea9',\n 300: '#fe9273',\n 400: '#fc5130',\n 500: '#fa3115',\n 600: '#eb170b',\n 700: '#c30c0b',\n 800: '#9b1116',\n 900: '#7d1114',\n 950: '#43070b',\n}\n```\n\nNow when I use `bg-flamingo`, I can always use `text-flamingo-fg` to get a valid contrasting color,\nno need for other checks or JS logic or what not.\n\n## Customize breakpoints\n\n### Mobile-first or desktop-first?\n\nTailwind is a mobile-first library. This approach leads to patterns which determine how you make\nyour designs responsive.\n\nFor example, you might design a section that turns from a horizontal flex on desktop, to a vertical\none on mobile.\n\nNormally, you would take the mobile design as the \"default\" approach, and then use a media query\nmodifier to add a desktop design, like so:\n\n```html\n<ul class=\"flex flex-col md:flex-row md:flex-wrap\">\n <li>...</li>\n</ul>\n```\n\nBut maybe you don't like that approach? Maybe you are developing a web app which is primarily used\non desktops, but you want to just make sure it's responsive enough to support smaller screens. Most\nof your overrides could be for smaller screens, and you might get designs for them first, while\nsmaller screens are an afterthought.\n\nIn that case, conside changing the way your media queries work for Tailwind. For example, I can find\nit handy in some projects, to have a setup like this:\n\n```ts\nexport default {\n theme: {\n screens: {\n '2xl': { max: '1440px' },\n xl: { max: '1280px' },\n lg: { max: '1024px' },\n md: { max: '800px' },\n sm: { max: '750px' },\n },\n },\n}\n```\n\nThis way, the above example can be rewritten like so:\n\n```html\n<ul class=\"flex flex-row flex-wrap md:flex-col\">\n <li>...</li>\n</ul>\n```\n\nThe difference in the example is subtle, but imagine using many breakpoints on one element or a few\nelements together, each using several breakpoints. It piles up!\n\nAn extension of this might be to combine `min` and `max` to only target specific screens, for\nexample:\n\n```ts\nexport default {\n theme: {\n screens: {\n mobile: { max: '760px' },\n tablet: { min: '761px', max: '1023px' },\n laptop: { min: '1024px', max: '1279px' },\n desktop: { min: '1280px' },\n },\n },\n}\n```\n\n## Add customized utilities\n\n## Use tw-merge\n\n## Add new animations\n\n## Avoid interpolated classes\n","collection":"post","data":{"title":"Making the most of TailwindCSS","showTitle":true,"includeLowResHero":false,"prose":true,"date":"2023-11-12T23:03:03.000Z","tags":"development css tips tailwind","draft":true}},"content":{"headings":[{"depth":2,"slug":"set-your-override-colors","text":"Set your override colors"},{"depth":3,"slug":"color-objects","text":"Color objects"},{"depth":3,"slug":"color-aliases","text":"Color aliases"},{"depth":3,"slug":"the-default-keyword","text":"The DEFAULT keyword"},{"depth":3,"slug":"contrast-colors","text":"Contrast colors"},{"depth":2,"slug":"customize-breakpoints","text":"Customize breakpoints"},{"depth":3,"slug":"mobile-first-or-desktop-first","text":"Mobile-first or desktop-first?"},{"depth":2,"slug":"add-customized-utilities","text":"Add customized utilities"},{"depth":2,"slug":"use-tw-merge","text":"Use tw-merge"},{"depth":2,"slug":"add-new-animations","text":"Add new animations"},{"depth":2,"slug":"avoid-interpolated-classes","text":"Avoid interpolated classes"}],"remarkPluginFrontmatter":{"title":"Making the most of TailwindCSS","date":"2023-11-13 01:03:03 +0200","tags":"development css tips tailwind","draft":true}}},{"params":{"slug":"2023-11-11-html-dialogs-using-astro-and-htmx"},"props":{"id":"2023-11-11-html-dialogs-using-astro-and-htmx.md","slug":"2023-11-11-html-dialogs-using-astro-and-htmx","body":"\n<!-- https://youtu.be/CYuujJvgmns?si=7dg3v2hDtrgtyvEP&t=1784 -->\n\n> \"[...] things like modals and multi-step UI &mdash; this part you're not doing in Astro.\"\n>\n> ~ Theo, on [this video](https://youtu.be/CYuujJvgmns?si=7dg3v2hDtrgtyvEP&t=1781)\n\nIs that a challenge?\n\nI'm sure it's not. And actually, for the most part, I guess you could say it's true. Astro is built\nmore for static websites with small interatcivity than for full-blown interactivity suite with user\nflows on a single page.\n\nBut it shouldn't necessarily be. Dialogs have been part of the HTML5 spec for a while, and it's not\ngoing away soon.\n","collection":"post","data":{"title":"Build HTML5 Dialogs using Astro and HTMX","showTitle":true,"includeLowResHero":false,"prose":true,"date":"2023-11-11T00:25:47.000Z","tags":"","draft":true}},"content":{"headings":[],"remarkPluginFrontmatter":{"title":"Build HTML5 Dialogs using Astro and HTMX","date":"2023-11-11 02:25:47 +0200","tags":"","draft":true}}},{"params":{"slug":"2023-11-09-create-your-own-use-optimistic-hook"},"props":{"id":"2023-11-09-create-your-own-use-optimistic-hook.md","slug":"2023-11-09-create-your-own-use-optimistic-hook","body":"\nRecently, I came across [this video](https://www.youtube.com/watch?v=M3mGY0pgFk0&t=279s) by Web Dev\nSimplified, which introduced me to React's new\n[useOptimistic](https://react.dev/reference/react/useOptimistic) hook.\n\nI began to wonder whether this is even needed. React has never been a library to provide more than\nbare-bones controls and hooks. The `useCallback`, `useMemo`, and `useState` hooks are most of the\nbuilt-in hooks and they provide a solid base upon which you can build pretty much any logic inside\nyour components or custom hooks.\n\nSo since when does React provide more high-level abstractions above hooks? At that point, they\nbecome even more opinionated than they already are. Not all use cases are the same - but this _is_ a\npretty generic hook, all things considered. Apparently, it is still in the experimental stage.\n\nI decided to experiment with creating this hook on my own, to understand if it was overcoming some\ninternal system limitation. I looked at various examples of existing optimistic hooks on NPM, but I\ndidn't really find one that falls in line with more modern coding styles, or they were completely\noutdated and abandoned.\n\n## Why create your own?\n\nIdeally, React should generally keep its size down as much as possible, and not increase the bundle\nwith more bloat that won't be used by everyone. I believe this hook is a very nice idea, but it is\nnot relevant for everyone, and I think if you find it does fit your needs, you don't have to wait\nfor official support - you can do it (or copy the final code here) in relatively short time, and\nfeature-complete (and added).\n\nIn addition, I thought it would make a good thought experiment and some coding practice, it can't\nhurt!\n\n## Looking at React's original implementation\n\nI decided to first take a look at React's official implementation of this. Not the source code, but\nI did want to get a sense of whether I like the consensus they came up with, or whether I feel it\nhas room for improvement.\n\nHere is a basic usage example, verbatim from\n[React's docs](https://react.dev/reference/react/useOptimistic#use):\n\n```ts\nconst [optimisticState, addOptimistic] = useOptimistic(\n state,\n // updateFn\n (currentState, optimisticValue) => {\n // merge and return new state\n // with optimistic value\n },\n)\n```\n\nSeems nice, but I think we can do better.\n\nOff the top of my head, I don't see a way to get the status of the optimistic value. Can I know when\nit's been finalized without having to maintain a separate state?\n\nAlso, there doesn't seem to be a way to roll back to the original value if an error has occurred. Or\nat least, the docs don't yet specify any. We can fix that, and provide a more \"holistic\" approach,\nwhich in my opinion doesn't leave the scope of this hook. Say I push an optimistic update, and it\nfails, how can I roll it back?\n\nWe'll try to make sense of where it should go as we go along.\n\n## Creating a hook\n\nLet's start with the signature, and make sure we have our types going for us.\n\n```ts\ntype StateUpdater<T, A = T> = (currentState: T, newState: A) => T\n\nexport function useOptimistic<T, A>(\n initial: T,\n merge?: StateUpdater<T, A>,\n): readonly [T, React.Dispatch<React.SetStateAction<T>>] {\n // ...\n}\n```\n\nWe start with the basics. An initial value, of type `T` which is generic, and an optional merge\nfunction. If it's not provided by the user, we'll just have it replace the old state with the new\nwhen an update comes. The `merge` function can have any type in the argument for the final dispatch,\nbut we assume `T` unless specified.\n\nNow we should add the state that maintains the optimistic value.\n\n```ts\nexport function useOptimistic<T>(\n initial: T,\n merge?: StateUpdater<T, A>,\n): readonly [T, React.Dispatch<React.SetStateAction<A>>] {\n const [state, setState] = useState(initial)\n return [state, setState]\n}\n```\n\nWe... have a very simple hook working right now. It doesn't really do anything special at all. Very\nbland.\n\nOk so, what's the most basic requirement? Well, for starters, it should let us merge the state if we\nprovide a `merge` function, which we haven't use yet from our arguments. Then, we should take into\naccount that updating the parent state (held in `initial`) should also update the state itself, so\nthat it will be up-to-date with the latest server value.\n\n## Creating the update functions\n\nWe'll start by adding the `useEffect` hook, that will update from the parent `initial` prop when it\nupdates.\n\n```ts\nuseEffect(() => {\n setState(initial)\n}, [initial])\n```\n\nNow, when the `initial` value is updated, the internal state will be kept up-to-date with the\nchange.\n\nWe can also implement the `merge` functionality. For that, we need to provide another implementation\nof the `setState` function, which uses it internally and adds some more logic.\n\n```ts\nconst update = useCallback(\n (value: SetStateAction<A>) => {\n const newValue = typeof value === 'function' ? (value as (value: T) => T)(state) : value\n const merged = merge ? merge(state, newValue as A) : (newValue as T)\n setState(merged)\n },\n [state],\n)\n\nreturn [state, update] // instead of [state, setState]\n```\n\nOkay, it's a little more to break down here.\n\nFirst, we have an identical callback to `setState` now, except it also runs the `merge` function\nbefore sending the final value.\n\nIn `useState`, in addition to passing a new state value, the `setState` function can be used with a\ncallback function, which takes the current state as a parameter, and should return the new state.\nThe variable `newValue` is populated with the result of that process.\n\nThen we just stack the `merge` function on top of that, if it exists.\n\nThe usage for this would still be:\n\n```ts\nconst [count, setCount] = useOptimistic(0)\n\n// option 1\nsetCount(1)\n\n// option 2\nsetCount((c) => c + 1)\n```\n\nAll the changes happen internally. Nice.\n\nThat's pretty much it as it stands right now. React's implementation is pretty similar to this, with\nmaybe a slight variation on how the change is calculated with the parent's update, to determine when\nthe value should be brought up to date.\n\nA bit anti-climactic? Well, let's add a few bells and whistles. And we can now, because we don't\nneed React to build it for us. We're strong!\n\n## What else can we add? Rolling back on failure\n\nThe first idea for improvement I have is to be able to catch an error, and roll back the value if it\nfails. Let's begin implementing this.\n\nFirst, we need to have a value to go back to. So every time the developer updates the optimistic\nstate to a new value, and an operation related to it fails, we want to provide a way to revert to\nthe original value.\n\nFor a recap, here's the entire hook as it now is, before we make changes:\n\n```ts\ntype StateUpdater<T, A = T> = (currentState: T, newState: A) => T\n\nexport function useOptimistic<T>(\n initial: T,\n merge?: StateUpdater<T, A>,\n): readonly [T, React.Dispatch<React.SetStateAction<A>>] {\n const [state, setState] = useState(initial)\n\n useEffect(() => {\n setState(initial)\n }, [initial])\n\n const update = useCallback(\n (value: SetStateAction<A>) => {\n const newValue = typeof value === 'function' ? (value as (value: T) => T)(state) : value\n const merged = merge ? merge(state, newValue as A) : (newValue as T)\n setState(merged)\n },\n [state],\n )\n\n return [state, update]\n}\n```\n\nNow, let's say the developer comes across a case like this:\n\n```tsx\nfunction MyApp({ comments }) {\n const [oComments, addComment] = useOptimistic(\n comments || [],\n (existingComments, newComment) => [...existingComments, newComment],\n )\n\n const sendComment = async (comment: Comment) => {\n addComment(comment)\n await sendCommentToApi(comment) // could fail and throw\n }\n\n return ...\n}\n```\n\nIn this case, we are sending some API request to send the comment content to the backend and make\nsure it's already there.\n\nBut if it fails, we will have to revert it to its original content manually by keeping a separate\nstate for the original value, and set it back if it failed.\n\nInstead, we can make our new hook handle this.\n\nLet's add another state that maintains the previous value:\n\n```ts\nconst [rollbackState, setRollbackState] = useState<T | null>(null)\n```\n\nAnd now that we have that, we should set this rollback value once we set a new value through the\nregular setter:\n\n```ts\nconst update = useCallback(\n (value: SetStateAction<A>) => {\n const newValue = typeof value === 'function' ? (value as (value: T) => T)(state) : value\n const merged = merge ? merge(state, newValue as A) : (newValue as T)\n setRollbackState(state)\n setState(merged)\n },\n [state],\n)\n```\n\nWe take this value, save it, and... Do nothing with it. We should probably write that logic\nsomewhere. How's this:\n\n```ts\nconst rollback = useCallback(() => {\n if (rollbackState == null) {\n // you can either return here, or throw an error, and be more strict about false rollbacks:\n // throw new Error('There is no state to roll back to')\n return\n }\n setState(rollbackState!)\n setRollbackState(null)\n}, [rollbackState])\n```\n\nAnd we can also add a `done` callback, which ditches the retained value, just for clean up.\nSomething like this:\n\n```ts\nconst done = useCallback(() => {\n setRollbackState(null)\n}, [])\n```\n\nLet's also return these in our hook:\n\n```ts\nexport function useOptimistic<T, A = T>(\n initial: T,\n merge?: StateUpdater<T, A>,\n): readonly [\n T,\n React.Dispatch<React.SetStateAction<A>>,\n {\n readonly rollback: () => void\n readonly done: () => void\n },\n] {\n // ...\n return [state, update, { rollback, done }]\n}\n```\n\nNice, right? We just revert. Now to actually use it, we can update the component:\n\n```ts\nconst [oComments, addComment, { done, rollback }] = useOptimistic(\n // ^^^^^^^^^^^^^^^^^^\n comments || [],\n (existingComments, newComment) => [...existingComments, newComment],\n)\n\nconst sendComment = async (comment: Comment) => {\n try {\n addComment(comment)\n await sendCommentToApi(comment) // could fail and throw\n } catch (e) {\n rollback()\n }\n}\n```\n\nThat's not too bad. If we fail, we use `rollback` and get back our previous value, and we never had\nto save it separately, which is good for scaling this to multiple places.\n\nBut... if you give it a go, you will notice a problem.\n\nLet's do a small test:\n\n```ts\nconst fail = async (comment: Comment) => {\n try {\n addComment(comment)\n await new Promise((res) => setTimeout(res, 1000))\n throw new Error('Oops!')\n done()\n } catch {\n rollback()\n }\n}\n```\n\nMake this run somewhere and you will realize the value reverted back to `null` instead of the old\nvalue, or simply doesn't roll back. What gives?\n\nHere is the problem: when we are using `rollback`, it uses the state variable, `rollbackState`, to\nset the value back on the main state. But here is the thing - this variable is a dependency in the\ncallback hook on `rollback`. Take a closer look:\n\n```ts\n// before:\n// const [rollbackState, setRollbackState] = useState<T | null>(null)\n//\n// after:\nconst rollbackState = useRef<T | null>(null)\n\nconst rollback = useCallback(() => {\n if (rollbackState == null) {\n return\n }\n setState(rollbackState!)\n setRollbackState(null)\n}, [rollbackState])\n// ^^^^^^^^^^^^^\n```\n\nSo why is that bad? Well, in our `fail` test above, we used `addComment` which is our `update`\nfunction with a fancy name. When the API call fails we call `rollback`. But `rollback` was created\nbound to the `rollbackState` that was there at the time of its creation. We `wait` for a promise to\nreturn, and then use the `done` or `rollback` functions, that were created **before** the\n`rollbackState` was even set away from `null` and into a value in the first place. So basically we\nare re-using the value from before we put anything in it, which means it's `null`, which is the\nvalue we roll back to when using `rollback`.\n\nHow do we fix this?\n\n## Fixing the stale value\n\nThere are probably many possible solutions, but here is my very naive one.\n\nHow do I make sure I get the correct reference to the updated value?\n\nIn reality, I don't need `rollbackState` to be an actual state. Why can I say that with confidence?\nWell, I don't need to call a re-render when it updates, as it is only there to reference a value in\nanother function when called later, and it's not displayed in the UI - meaning it has no need to\ncall for a render when I update it. Also it happens on literally every `update` call as well, so I\nam trying to uselessly mark the frame dirty for re-render twice when one time is enough.\n\nWith that in mind, I can now safely switch the `rollbackState` from a state hook to a `useRef` hook.\n\nWhy would that work? Because a `useRef` reference is always a constant object. But the `current`\nvalue inside that object can be changed. In effect, when we reference a ref value, we can say we are\nsure it is the most recent, as the ref never needs to change and no mismatches can occur.\n\nHere's how it looks after the fix:\n\n```ts\nconst update = useCallback(\n (value: SetStateAction<A>) => {\n const newValue = typeof value === 'function' ? (value as (value: T) => T)(state) : value\n const merged = merge ? merge(state, newValue as A) : (newValue as T)\n rollbackState.current = state\n setState(merged)\n },\n [state],\n)\n\nconst rollback = useCallback(() => {\n if (rollbackState.current == null) {\n return\n }\n setState(rollbackState.current!)\n rollbackState.current = null\n}, [])\n\nconst done = useCallback(() => {\n rollbackState.current = null\n}, [])\n```\n\nThis way, no matter when I call the `rollback` or `done` functions, they will always try to access\nthe same parent object. With that object, they can modify the inner property and we don't need to\nworry about the data being stale.\n\n## Simplifying usage\n\nYou might notice that now you can have a pretty redundant pattern of `try {...} catch() {...}` where\nyou would `rollback` and `done` at the end of each section. Well that's no fun, we want less code.\n\nWhy not add this to our hook?\n\n```ts\nconst run = useCallback(\n async (cb: () => Promise<unknown> | unknown) => {\n try {\n await cb()\n done()\n } catch (_) {\n rollback()\n }\n },\n [done, rollback],\n)\n```\n\nSimple yet elegant. If we export this from the hook:\n\n```ts\nreturn [state, update as React.Dispatch<React.SetStateAction<A>>, { done, rollback, run }]\n```\n\nNow it will now let us do:\n\n```ts\nconst sendComment = async (comment: Comment) => {\n run(async () => {\n addComment(comment)\n await sendCommentToApi(comment)\n })\n}\n```\n\nMuch simpler, right?\n\n## Saving the promise state\n\nOne last thing I want us to add is the ability to know when our state is loading. This way, even\nwithout using `useMutation` from\n[@tanstack/react-query](https://www.npmjs.com/package/@tanstack/react-query), we can get a loading\nstate of any promise, while working directly with the `useOptimistic` hook instead.\n\nFor that we can simply add a state or a ref (decide which you want depending on your wanted DX),\nwhich sets a `pending` state to `true` and `false` when we update the state, and when we call\ndone/rollback, accordingly.\n\nHere is the full entire hook with all the things we made here:\n\n```ts\ntype StateUpdater<T, A = T> = (currentState: T, newState: A) => T\n\nexport function useOptimistic<T, A = T>(\n initial: T,\n merge?: StateUpdater<T, A>,\n): readonly [\n T,\n React.Dispatch<React.SetStateAction<A>>,\n {\n readonly done: () => void\n readonly rollback: () => void\n readonly pending: boolean\n readonly run: (promise: () => Promise<unknown> | unknown) => void\n },\n] {\n const [state, setState] = useState(initial)\n const rollbackState = useRef<T | null>(null)\n const [pending, setPending] = useState(false)\n\n useEffect(() => {\n setPending(false)\n setState(initial)\n }, [initial])\n\n const update = useCallback(\n (value: SetStateAction<A>) => {\n const newValue = typeof value === 'function' ? (value as (value: T) => T)(state) : value\n const merged = merge ? merge(state, newValue as A) : (newValue as T)\n rollbackState.current = state\n setState(merged)\n setPending(true)\n },\n [state],\n )\n\n const rollback = useCallback(() => {\n if (rollbackState.current == null) {\n // throw new Error('There is no state to roll back to')\n return\n }\n setState(rollbackState.current!)\n rollbackState.current = null\n setPending(false)\n }, [])\n\n const done = useCallback(() => {\n rollbackState.current = null\n setPending(false)\n }, [])\n\n const run = useCallback(\n async (cb: () => Promise<unknown> | unknown) => {\n try {\n await cb()\n done()\n } catch (_) {\n rollback()\n }\n },\n [done, rollback],\n )\n\n return [\n state,\n update as React.Dispatch<React.SetStateAction<A>>,\n { done, rollback, pending, run },\n ] as const\n}\n```\n\n## Where to go from here?\n\nThere are many ways that you can improve this hook, but I feel like it now reached a fairly usable\nstate which we can all build from in our own special ways.\n\nUntil React decides (or decides against) promoting `useOptimistic` to non-canary channels, maybe we\ncan all benefit from a relatively simple hook that does exactly this without having to wait.\n\nI hope you found this article useful or interesting. Feel free to ask questions or suggest\nimprovements in the comments!\n","collection":"post","data":{"title":"Create your own useOptimistic hook","showTitle":true,"image":"/images/post_covers/sad-happy.jpg","imageAttribution":"Image by \n<a href=\"https://www.freepik.com/free-photo/unhappy-offended-woman-stands-with-arms-crossed-doesnt-speak-husband-joyful-bearded-man-has-dark-skin-giggles-positively-something-funny-expresses-positive-emotions-people-reaction_13580391.htm\">\n wayhomestudio\n</a>\non Freepik","includeLowResHero":false,"prose":true,"date":"2023-11-09T21:38:07.000Z","tags":"react javascript typescript tutorial development"}},"content":{"headings":[{"depth":2,"slug":"why-create-your-own","text":"Why create your own?"},{"depth":2,"slug":"looking-at-reacts-original-implementation","text":"Looking at React’s original implementation"},{"depth":2,"slug":"creating-a-hook","text":"Creating a hook"},{"depth":2,"slug":"creating-the-update-functions","text":"Creating the update functions"},{"depth":2,"slug":"what-else-can-we-add-rolling-back-on-failure","text":"What else can we add? Rolling back on failure"},{"depth":2,"slug":"fixing-the-stale-value","text":"Fixing the stale value"},{"depth":2,"slug":"simplifying-usage","text":"Simplifying usage"},{"depth":2,"slug":"saving-the-promise-state","text":"Saving the promise state"},{"depth":2,"slug":"where-to-go-from-here","text":"Where to go from here?"}],"remarkPluginFrontmatter":{"title":"Create your own useOptimistic hook","date":"2023-11-09 23:38:07 +0200","tags":"react javascript typescript tutorial development","image":"/images/post_covers/sad-happy.jpg","imageAttribution":"Image by \n<a href=\"https://www.freepik.com/free-photo/unhappy-offended-woman-stands-with-arms-crossed-doesnt-speak-husband-joyful-bearded-man-has-dark-skin-giggles-positively-something-funny-expresses-positive-emotions-people-reaction_13580391.htm\">\n wayhomestudio\n</a>\non Freepik"}}},{"params":{"slug":"2023-11-05-why-tooling-is-important"},"props":{"id":"2023-11-05-why-tooling-is-important.md","slug":"2023-11-05-why-tooling-is-important","body":"\n<!-- I am seriously lazy. It sometimes affects my life, and not in a great way. But one thing I know for\nsure, is that is serves me extremely well in my work as a Full Stack&trade; developer. Let me\nexplain why.\n\nSoftware takes time. No change is 0 seconds to make. With that in mind, I still prefer to spend as\nlittle time doing as much as possible, in order to min-max my effort, get something done (and well),\nand go back to whatever it is I want to do otherwise in my life, or another task.\n\nThis has caused me to spend countless hours tweaking my setup. Writing one-off functions that are\ntoo-specific to actually be used, or some that are actually useful. Writing a script that I imagined\nwould be run every week or so and ends up being touched maybe twice.\n\nAll of these are failures. Or are they? Every file I made, every second I \"wasted\" ended up giving\nme more than taking in the end: The lessons I learned making those mistakes, finding out whether my\nsolution worked or not, discovering bugs and fixing along the way, writing bugs in that same script\nand learning to avoid them later on.\n\nBut more importantly than all of those - is the macro understanding of what's important to spend\ntime on, what's not, and what time I can spend to avoid spending more of it later.\n\n## Tooling is sometimes under-used, or ignored\n\nWhenever I have to dabble with Python, I learn the same painful lesson. The tooling sucks. And it's\nnot until you have something you truly hate, to remind you of what you love and what you're missing\nright now.\n\nInstalling packages, making sure you have up to date dependencies, these are things you will\ninevitably do from time to time. And it's important that you have as little active time working on\nusing those tools as possible. Ideally, you hit <kbd>Enter</kbd> and go do something else entirely.\n\nThese things should be trivial, and yet they're not. Why? If one language has it down, others can\nfollow suit. Yet, Python is stuck somewhere in 2012 with the quality of the package ecosystem when\nyou actually need to get a project working where you aren't the first developer on it. You will\nstruggle, you will install the wrong stuff, others won't install at all because it's not listed\nproperly, versioning is willy-nilly and people don't lock versions for stuff. It's so frustrating.\n\nIt might be the actual developers making those mistakes, and there might be better ways to do these\nthings. But after trying it out several times myself, I realized the user is always stupid, and the\ntool should always be right. That's why it's on a computer. If I make a calculator, for example, I\nshould make it auto-close parenthesis if the user forgot. The same goes for tools. While there is a\nlot to be said for learning to develop the hard way, it shouldn't be the same approach when using\nbuild tools. They should just work, and let you get to your actual code fast. But for some reason,\nit's not trivial to do these simple things, and you end up doing devops engineering just to get your\nwork project to run locally without error.\n\n## DX is a buzzword, and it's the only one I like\n\nDeveloper experience, commonly abbreviated DX (in contrast to user experience, or UX), has been a\nterm floating around the internet in the last few years.\n\nSome people say it's a fancy word for making things clear, and some say it is the most important\ntask in the entire world. Neither of them are entirely right or entirely wrong. Like with\neverything, the truth is subjective, and lands somewhere in the middle.\n\nPersonally, I think that more than a buzzword, and more than just meaning \"make stuff easy to use\nfor devs\". There is a philosophy that stands behind it. And somewhere in that philosophy, is the\nunderstanding that in order to be a good tool, you need not to only be easy to use and clear, but\nyou have to design your tool for building up success.\n\nWhat does that mean?\n\nIt means you need to care for the way you design your user's (the dev's) experience using that tool.\nOn top of making easy abstractions for your functions, I believe you need to embrace making it\nactually friendly, and let your tool remember that while it's powerful, a person has to use it.\n\nErrors need to be extremely clear, and indicate where the problem is. Things should be color coded.\nTell me what you're doing. Enforce sensible defaults, and let us easily change them when needed.\n\nIt's like in the chase of being un-opinionated, sometimes, things are made with so many variations,\nnone can be fully accepted and the fighting will go on between them forever. It's okay to embrace\nthe user's wishes and try to find ways to make them happen. It's okay to maybe add some default that\n99% of the users would use anyway, instead of requiring 99% of users to make mistakes in order to\nreach that value on their own. This isn't programming - there shouldn't be a skill for using a basic\ntool that installs stuff, or generates some files. It should be some dry knowledge, that is somewhat\nuniversal, that is easy to apply and remember.\n\n## Not just build tools\n-->\n\n## Good tools can be a learning tool\n","collection":"post","data":{"title":"Why tooling is important","showTitle":true,"image":"/images/post_covers/tooling-robot.jpg","imageAttribution":"Background by \n<a \n href=\"https://www.freepik.com/free-vector/game-landscape-with-tropical-plants_9886200.htm#query=cartoon%20jungle&position=0&from_view=keyword&track=ais\">\n valadzionak_volha\n</a>\non Freepik","includeLowResHero":false,"prose":true,"date":"2023-11-05T21:37:25.000Z","tags":"development tool","draft":true}},"content":{"headings":[{"depth":2,"slug":"good-tools-can-be-a-learning-tool","text":"Good tools can be a learning tool"}],"remarkPluginFrontmatter":{"title":"Why tooling is important","date":"2023-11-05 23:37:25 +0200","tags":"development tool","draft":true,"image":"/images/post_covers/tooling-robot.jpg","imageAttribution":"Background by \n<a \n href=\"https://www.freepik.com/free-vector/game-landscape-with-tropical-plants_9886200.htm#query=cartoon%20jungle&position=0&from_view=keyword&track=ais\">\n valadzionak_volha\n</a>\non Freepik"}}},{"params":{"slug":"2023-11-05-the-simplicity-of-astro"},"props":{"id":"2023-11-05-the-simplicity-of-astro.md","slug":"2023-11-05-the-simplicity-of-astro","body":"\nRecently I've decided to undertake redesigning this website. After years of using Jekyll to serve\nstatic websites, I have been looking for a change. A more modern way to serve my static sites, which\nwill make it easier and more predictable to host sites for my projects, this blog, and any future\nendeavors on the web which might not require a full-blown app or server.\n\n[Jekyll](https://jekyllrb.com/) is great - there is community support with years of questions and\nanswers to look up, many plugins for various uses, and themes to make your site look nice and\npretty. That said, it has begun to show its age. It's becoming hard to incorporate newer libraries\nsuch as [Tailwind](https://tailwindcss.com), it's not very fun to generate dynamic content using\nliquid templates, it's up to you to handle your styling composition and scopes, and the whole build\nsystem is just a bit outdated, requiring old-school manual work where automation is ripe in other\nweb frameworks.\n\nThat is not to downplay Jekyll - I still love it, and have used it for several years exactly because\nit was relatively easy to set up, it was easy at the time to get a theme running and customize it a\nbit. Using Markdown files to generate content on your site is simply a brilliant idea. Who wants a\ngiant tree of `<span>`s and `<div>`s for a simple blog article? This also opens up the possibility\nto use many more applications for writing the posts themselves, as Markdown is exportale to from\nmany applications.\n\nOn top of all that, GitHub works with it out-of-the-box, so it's incredibly simple to spin up a\nrepository, and be able to place some dynamic code for some static output. It's great!\n\n## Do less to create more\n\nAnd still, it was time to move on. I wanted to spend less time finding out where I was missing some\nliquid syntax. I wanted to use Prettier to format stuff, and I wanted templated sections to be\nsyntax highlighted. I have become spoiled because of React, and using JSX would also be a bonus to\nme.\n\nI began questioning, why can't I just use React to spin up a static website? No SSR with smart\ncaching (love you, [Next.js](https://nextjs.org)), no single page application where I need various\nhacks to get routing to properly work. Just let me write React-like stuff and get some HTML with JS\nprops in them. That's really all I'm asking.\n\n## And Astro Delivered\n\nI have heard about it a while ago from listening to some frontend related podcasts. I didn't really\nget the point of it, till I decided to give it a go one evening. And it's just what I was looking\nfor.\n\nIt's a simple, easy to support and understand template syntax, which gives me both the freedom of\nusing TS/JS to develop my logic, and the composable nature of React, while still delivering an\nincredibly fast static web application which **just works**.\n\nLong-gone are the days of formatters jumbling up every time I add a `{%if}` to my template.\nLong-gone are the days of including whole page sections, and needing to override global variables\njust to get a semblance of the ability to re-use the same code easily with different data, without\nall the hacking.\n\n## What is simple?\n\nWhen I first tried Astro, I was astounded at how little I needed to learn to get going.\n\nThe routing is a breeze. Creating collections of data was incredibly easy. Adding complex dynamic\npermalinks, using logic-filled templates, it's all just simple Node-like code that you put on top of\nthe page you're handling.\n\nBut it goes beyond this. Astro gives you just enough to get the information you need from the\ncontext of the page, and spit it out in various forms.\n\nWhen you create a list of pages that represent posts, you don't do it in a magic \"\\_posts\"\ndirectory - you define that yourself. And it's just an array you export from a function, with the\ndata for the params of the page URL - that's it. Here's how generating posts look for a blog like\nthis:\n\n```ts\nexport async function getStaticPaths() {\n const pages = await getCollection('page')\n return pages.map((page) => ({\n params: { slug: page.slug },\n props: page,\n }))\n}\ntype Props = CollectionEntry<'post'>\nconst page = Astro.props\nconst { Content } = await page.render()\n```\n\nYep... That's it. You get an array of posts. You put the URL params and the props it gets. That's\nit.\n\nI have implemented a tag directory, a projects directory, and pagination within a small amount of\nhours, totalling less than a work day.\n\n**The Astro team heavily invested time in making sure developers don't need to write a lot of\nboilerplate.**\n\nSure, the templates are usable only for Astro. But the content in those templates is almost\nbare-bones TS, and the methods for configuring and getting data of various types from all your\ngenerated pages is abstracted so simply that you barely use more than 1 function to get all your\nstuff done for an entire page collection.\n\nComparing to Jekyll, I remember wanting to add an automatic tags collection system, which generates\na tags list, and a list of posts for each tag. This required me to go and find out how to build\nplugins for Jekyll, so I can hook into the build-step, and get that code to operate while I run the\ndev server.\n\nIn contrast, when using Astro it is not only implied you define how your routes and information gets\nbuilt, it's a requirement for setting it up - there is no inherent \"\\_posts\" directory which is the\nmain entry point for blogs, and there is not much magic going on. You write, in the page that lays\nout the post template, exactly what pages you want to have.\n\nThen showing them all on the front page is even easier, like so:\n\n```ts\nconst allPosts = (await getCollection('post')).sort(\n (a, b) => b.data.date.valueOf() - a.data.date.valueOf(),\n)\nconst posts = allPosts.slice(0, PAGE_SIZE)\n```\n\nAnd you have a paginated list of posts. It's so easy! After this, you look over them in JSX. No\nliquid, no Handlebars, just JSX, which is already so widely supported.\n\n```tsx\n<div>\n {posts.map((post) => (\n <article>...</article>\n ))}\n</div>\n```\n\n## It's the philosophy\n\nEverything in Astro is just as simple. Scoping your CSS is no longer your concern - just add a\n`<style>` element, put whatever you need in it (transpilation supported!), and you treat it like a\nwhole page. You don't worry about class names, you re-use them as you want and not worry about\nconflicts in other places on the same page. They are auto scoped.\n\nIt really is a symptom of the best disease. I don't want to work hard. I want to write simple HTML\nor Markdown, and when I need just a tiny bit of logic, I can just put it there, and it took care of\nthe annoying stuff for me.\n","collection":"post","data":{"title":"The simplicity of Astro","showTitle":true,"includeLowResHero":false,"prose":true,"date":"2023-11-04T22:15:52.000Z","tags":"","draft":true}},"content":{"headings":[{"depth":2,"slug":"do-less-to-create-more","text":"Do less to create more"},{"depth":2,"slug":"and-astro-delivered","text":"And Astro Delivered"},{"depth":2,"slug":"what-is-simple","text":"What is simple?"},{"depth":2,"slug":"its-the-philosophy","text":"It’s the philosophy"}],"remarkPluginFrontmatter":{"title":"The simplicity of Astro","date":"2023-11-05 00:15:52 +0200","tags":"","draft":true}}},{"params":{"slug":"2022-08-19-react-using-use-effect-effectively"},"props":{"id":"2022-08-19-react-using-use-effect-effectively.md","slug":"2022-08-19-react-using-use-effect-effectively","body":"\nWhether you're new to React or you are recently migrating from class components (this late?), you\nmight not be familiar with how `useEffect` works.\n\nActually, you might be using it still from other existing (or copied 😉) code, but have no idea (or\njust a small idea) what's actually happening.\n\nIn this post we'll dive in to this very flexible hook, some example, and some real, common\nuse-cases.\n\n<!-- more -->\n\n## What is `React.useEffect`?\n\nThis is a React hook that lets you introduce **side-effects** into your components.\n\nHere is some example code of a common `useEffect` hook:\n\n```tsx\nconst MyComponent = () => {\n const [posts, setPosts] = React.useState([])\n\n // This will fire only once when the component mounts\n React.useEffect(() => {\n PostsAPI.getPostsList().then((data) => setPosts(data))\n }, [])\n return <div>{JSON.stringify(posts)}</div>\n}\n```\n\n### What are side effects?\n\nWell, side effects are a general programming term, under the larger term \"effects\". Effects have a\nwide meaning, and side-effects are a subset of that. The short version of the definition of a\nside-effect is \"a function (or piece of code) that introduces changes to any value that is not\ninside its scope\" - whether input arguments, or outside scoped variables (or global variables).\n\nIn React components, that mostly means any function inside a component that introduces a change that\nis outside the scope of rendering itself.\n\nAre you calling an outside API? You are introducing an effect. Saving a value to the state?\nIntroduces an effect. _As long as it's outside the scope of rendering the component itself, it's a\nside-effect._\n\nSide-effects **are not bad per-se** - but introducing them needs to be done with intent, and with\ncaution.\n\n### React side-effects example\n\n`React.useEffect` is a hook that lets you wrap side-effects in a safe way so that you can avoid\nunnecessary renders, and use the necessary ones correctly. Consider the following code:\n\n```tsx\nconst MyComponent = () => {\n const [count, setCount] = React.useState(0)\n\n CounterAPI.getCount().then((data) => setCount(data))\n\n return <div>{count}</div>\n}\n```\n\nIf you haven't noticed th the problem at first glance - don't worry.\n\nWhat happened here is we implicitly introduced a side effect right inside our rendering.\n\nAs soon as the component renders, the `CounterAPI` will get called, return something, and `setCount`\nwill trigger a state change, which causes a render.\n\nThen immediately, the new render runs, and another API call is made... This will go on forever, so\nReact (or your browser) will crash in some way, killing the page.\n\nThis is why we need to guard against side-effects. Here is a simple change to fix this:\n\n```tsx\nconst MyComponent = () => {\n const [count, setCount] = React.useState(0)\n React.useEffect(() => {\n // add this line\n CounterAPI.getCount().then((data) => setCount(data))\n }, []) // and this line\n return <div>{count}</div>\n}\n```\n\nThis will mean our API only calls once.\n\n### A replacement for lifecycle methods\n\nIf you have ever used class-based components in React, you might be familiar with the term. In these\ntypes of components, sometimes we would need to fire events when the component mounts for the first\ntime, or when one of the props changes, to sync our state properly with our business-logic.\n\nThis is the same effect from before, but in the old\\* method:\n\n```tsx\nclass MyComponent extends React.Component {\n render() {\n // return ...\n }\n\n componentDidMount() {\n MyAPI.getResults().then((data) => this.setState({ apiData: data }))\n }\n}\n```\n\n<p style=\"font-size: 0.8em\">\n* It's not deprecated, and React will support it for a while to come. But class-based components\nare generally considered to be outdated, a bit less easy to predict sometimes - especially for\nnewcomers, and generally too verbose for sometimes really simple code.\n</p>\n\nThe `useEffect` hook is also the de-facto replacement to the lifecycle methods, which are\nunavailable when you are using function components.\n\nLet's dive into how it's used.\n\n## Structure of the `useEffect` hook\n\nA `useEffect` call takes 2 arguments.\n\n```tsx\nReact.useEffect(\n () => {\n // the side-effect code\n\n // Optionally return a clean-up function\n return () => doCleanup()\n },\n [\n /* dependencies */\n ],\n)\n```\n\nThe first is a function to run at... \"some time\". The time in which to run this effect differs on\nyour use case, and your usage of the 2nd argument. We will talk about how it works shortly.\n\nThe second is optional - but in my opinion, very required - the dependency array. Like many React\nhooks ([see my post about `useMemo` and `useCallback`][memo-hooks]), it is a list of changes that\nthe component listens to, only running the effect after changes to the dependencies were detected.\n\nIn the case of `useEffect`, the effect always runs at least once, even if that means all the values\nit depends on might be `undefined`. So wait, how does this work?\n\n## Thinking \"Reactive\"\n\nThis is a bit confusing. We are listening to changes now - okay. I will elaborate a bit.\n\nIf in the past we used to rely on the framework to call specific methods in different lifecycle\ntimes, now we are going to change our way of thinking a bit. This method introduced many problems,\nespecially because developers were often introducing side-effects where they shouldn't be possible,\nintroducing bugs and other problems.\n\nMany hooks now rely on dependency arrays to define when they are being re-built. Our way of thinking\nwill be similar - our function will run when changes are made, and they're always made at least\nonce - in the mounting phase.\n\nIf we want to fire code once when our component is mounted, we can introduce an effect with 0\ndependencies. This causes it to only run once, as no changes will ever be introduced to this very\nconstant array. This causes the hook to behave exactly as `componentDidMount` did.\n\n### Dependencies: `[] !== undefined`\n\nIt's important to note that supplying **no array** in the dependency argument, and supplying an\n**empty array** are 2 different things! Try it out yourself to see the difference.\n\n```tsx\nconst MyComponent = () => {\n const [count, setCount] = React.useState(0)\n\n React.useEffect(() => console.log('no array'))\n React.useEffect(() => console.log('empty array'), [])\n\n return <div onClick={() => setCount((prev) => ++prev)}>{count}</div>\n}\n```\n\nRunning a component with this code will show different results.\n\nImmediately, we will see this in the log:\n\n```\nno array\nempty array\n```\n\nNothing special yet. Let's click the `div` and see what happens.\n\n```\nno array\n```\n\nAhh... What's this? It ran again? But we supplied no dependencies?\n\nSupplying no value at all means it will update **every time the component renders**, not just once.\nThe dependency needs to be an array for any comparison to happen. If it's `undefined`, comparison is\nskipped and the effect runs every time.\n\n## How can we use this now?\n\nLet's review the basics we learned about this hook.\n\n1. It runs at least once\n2. It then runs:\n 1. every time its dependency array contents change, or\n 2. if there is no array, after every render\n\n### Component mounting\n\nThe simplest effect we learned about is commonly referred to as a mount effect. It runs once since\nyou supply no dependencies.\n\nLet's say we are viewing a list of posts.\n\n```tsx\nconst PostList = () => {\n const [posts, setPosts] = React.useState([])\n const [page, setPage] = React.useState(0)\n\n React.useEffect(() => {\n setLoading(true)\n PostsAPI.getPosts({ page: page }).then((data) => {\n setPosts(data.posts)\n setLoading(false)\n })\n }, [])\n\n return (\n <div>\n {loading\n ? 'Loading...'\n : posts.forEach((post, i) => {\n return <div key={post.id}>{post.title}</div>\n })}\n </div>\n )\n}\n```\n\nThis will fetch the posts once, and once only.\n\nThen we have more cases, where we decide what to supply, and we can make our component react to\nchanges in either the props or change, and introduce the effects we need.\n\nLet's consider an extension to this use-case to test with.\n\n### Reacting to a state change\n\nWe will add a page selector, which lets us change which page of the posts we are viewing. For the\nsake of simplicity, the count per page is implicit and hard-coded in the backend.\n\n```diff\n const PostList = () => {\n const [posts, setPosts] = React.useState([])\n const [loading, setLoading] = React.useState(true)\n+ const [page, setPage] = React.useState(0)\n\n React.useEffect(() => {\n setLoading(true)\n PostsAPI.getPosts({ page: page }).then((data) => {\n setPosts(data.posts)\n setLoading(false)\n })\n- }, [])\n+ }, [page])\n\n return (\n <div>\n+ <span onClick={() => setPage(prev => ++prev)}>Next page &raquo;</span>\n {loading\n ? 'Loading...'\n : posts.forEach((post, i) => {\n return <div key={post.id}>{post.title}</div>\n })}\n </div>\n )\n }\n```\n\nAs you can see, we supplied our `page` as a dependency for the API call. This means that once the\ncomponent mounts, and then every time this count changes afterwards, React will run our effect\nagain, fetching the posts from the API. Neat!\n\n### The cleanup function, timeouts and intervals\n\nAnother great use-case is to run some logic on a fixed interval, or after a timeout. Let's say we\nwant to increase a counter every second.\n\n```tsx\nconst Counter = () => {\n const [count, setCount] = React.useState(0)\n\n React.useEffect(() => {\n setInterval(() => {\n setCount((prev) => ++prev)\n }, 1000)\n }, [])\n\n return <div>{count}</div>\n}\n```\n\nThis works great! Every second, we get a new count with a larger number.\n\nBut this introduces a bug: navigate to another route, or get this component removed in some way. You\nwill notice the interval keeps running!\n\nIn a case like this, we want to make sure our interval or timeout stops when the component is no\nlonger mounted, in order to prevent a memory leak.\n\nThis is where **cleanup functions** come in handy. A `useEffect` call can also accept a function as\na result of its callback.\n\nThat function will run once one of the dependencies change, before the \"new\" effect runs. It will\nalso run once the component **unmounts**, i.e., is being removed from the tree in some way. These\ncases are the same phase that calls the cleanup function, with the only difference being whether a\nnew effect will replace the discarded one, or will the effect end its life there.\n\nThis allows us to do things like cancel timers, API requests, or make some other cleanup to make\nsure nothing happens once it's no longer necessary. React class-component users will recognize this\nas being a similar use-case for using `componentWillUnmount` lifecycle method. Let's see how we can\nfix our previous example:\n\n```diff\n const Counter = () => {\n const [count, setCount] = React.useState(0)\n\n React.useEffect(() => {\n- setInterval(() => {\n+ const id = setInterval(() => {\n setCount((prev) => ++prev)\n }, 1000)\n+ return () => clearInterval(id)\n }, [])\n\n return <div>{count}</div>\n }\n```\n\nAs you may or may not already know, `setInterval` and `setTimeout` return an id token that we can\nlater use to cancel it. By supplying it as a return function to `useEffect`, we are telling it to\ndestroy our timer every time it needs to. If it is supposed to create a new one due to dependency\nchange, it will still create it and our code will still work.\n\n### External listeners\n\nThe last prominent use-case we will show here is using external listeners inside components.\n\nThis is similar to our previous use-case, where we have a function with a side-effect that needs to\nbe cleaned up. In fact it's pretty much exactly the same. Let's say we want to display the window\nresolution in our component (or make some condition depend on a value from there). Let's use our\nprevious knowledge to make a component that works for this.\n\n```tsx\nconst WindowSize = () => {\n const [width, setWidth] = React.useState(0)\n const [height, setHeight] = React.useState(0)\n\n React.useEffect(() => {\n const listener = () => {\n setWidth(window.innerWidth)\n setHeight(window.innerHeight)\n }\n window.addEventListener('resize', listener)\n return () => window.removeEventListener('resize', listener)\n }, [])\n\n return (\n <div>\n Width: {width}px\n <br />\n Height: {height}px\n </div>\n )\n}\n```\n\nNow we have an effect that listens to window changes, and makes sure to update the state with it.\nAnother success! And as you can see, it's not much different than the interval/timeout example.\n\n## Conclusion\n\nI hope that now you might see a bit more clearly how this hook is useful, and how to actually put it\nto use properly.\n\nExperiment with using `useEffect` in different cases, and experiment with thinking more reactively\nabout changes - making dependencies change things as they need, instead of always chaining function\ncalls whenever you change values just to replicate the same behavior.\n\n<!-- -->\n\n[memo-hooks]: /2022/08/how-to-use-react-memoization-hooks-for-increased-performance\n\n<!-- -->\n","collection":"post","data":{"title":"React: Using useEffect Effectively","showTitle":true,"image":"/images/post_covers/pexels-yasin-gundogdu-2709563.jpg","includeLowResHero":false,"prose":true,"date":"2022-08-18T21:00:00.000Z","tags":"react tutorial javascript typescript development"}},"content":{"headings":[{"depth":2,"slug":"what-is-reactuseeffect","text":"What is React.useEffect?"},{"depth":3,"slug":"what-are-side-effects","text":"What are side effects?"},{"depth":3,"slug":"react-side-effects-example","text":"React side-effects example"},{"depth":3,"slug":"a-replacement-for-lifecycle-methods","text":"A replacement for lifecycle methods"},{"depth":2,"slug":"structure-of-the-useeffect-hook","text":"Structure of the useEffect hook"},{"depth":2,"slug":"thinking-reactive","text":"Thinking “Reactive”"},{"depth":3,"slug":"dependencies---undefined","text":"Dependencies: [] !== undefined"},{"depth":2,"slug":"how-can-we-use-this-now","text":"How can we use this now?"},{"depth":3,"slug":"component-mounting","text":"Component mounting"},{"depth":3,"slug":"reacting-to-a-state-change","text":"Reacting to a state change"},{"depth":3,"slug":"the-cleanup-function-timeouts-and-intervals","text":"The cleanup function, timeouts and intervals"},{"depth":3,"slug":"external-listeners","text":"External listeners"},{"depth":2,"slug":"conclusion","text":"Conclusion"}],"remarkPluginFrontmatter":{"title":"React: Using useEffect Effectively","date":"2022-08-19 00:00:00 +0300","tags":"react tutorial javascript typescript development","image":"/images/post_covers/pexels-yasin-gundogdu-2709563.jpg"}}},{"params":{"slug":"2022-08-10-how-to-use-react-memoization-hooks-for-increased-performance"},"props":{"id":"2022-08-10-how-to-use-react-memoization-hooks-for-increased-performance.md","slug":"2022-08-10-how-to-use-react-memoization-hooks-for-increased-performance","body":"\nAs React apps grow bigger and more complex, performance becomes more and more of a problem. As\ncomponents become larger and contain more and more sub-components, rendering becomes slow and turns\ninto a bottleneck.\n\nHow do we tackle this? If you haven't used `useMemo` and `useCallback`, we can start with those.\n\n<!-- more -->\n\nIn this tutorial, we will take a look at how these 2 very easy and handy callbacks work, and why\nthey are so useful. In fact, these days my eyes get sore when I don't see them used. So let's dive\nin to what they do.\n\n<hr class=\"center\" />\n\n## React.useMemo\n\nThis React hook's one goal is to save a value for later use, and not re-calculating it on the spot.\n\nLet's take an example of some expensive logic that runs in our render function:\n\n```tsx\nconst ExpensiveComponent: React.FC = (props) => {\n const [list, setList] = React.useState([])\n const [counter, setCounter] = React.useState(0)\n const multiplied = list.map((i) => (i * 972 + 1000) / 5213).join(', ')\n\n function addRandom() {\n setList((prev) => [...prev, Math.floor(Math.random() * 10000)])\n }\n\n function increaseCounter() {\n setCounter((prev) => ++prev)\n }\n\n return (\n <div>\n Counter: {counter}\n <br />\n Multiplied: {multiplied}\n <br />\n <button onClick={addRandom}>Add Random</button>\n <button onClick={increaseCounter}>Increase Counter</button>\n </div>\n )\n}\n```\n\nDoesn't seem very problematic, but take a look at the `multiplied` variable. The logic in this\nexample isn't too bad, but imagine working with a giant list of special objects. This mapping alone\ncould be a performance problem, especially if it's looped in a parent component.\n\nIn this case, there is another state hook - `counter`. When `setCounter` is called, `multiplied`\nwill be calculated all over again, wasting previous resources, even when an update in that case is\nnot needed as these variables are independent of each-other.\n\nThat's where `useMemo` comes in hand (read the\n[official docs](https://reactjs.org/docs/hooks-reference.html#usememo)).\n\nYou can use this hook to save the value, and retrieve the same object until a re-calculation is\nneeded.\n\nHere's how it's used, the only line we need to change is the `multiplied` definition:\n\n```tsx\nconst multiplied = React.useMemo(() => {\n console.log('recalculating multiplied:', list)\n return list.map((i) => (i * 972 + 1000) / 5213).join(', ')\n}, [list])\n```\n\nThe `useMemo` hook takes 2 arguments:\n\n1. The `create` function - used to return the calculated value of the variable we want to eventually\n use\n2. A list of dependencies. The dependency list is used to determine **when** a new value should be\n calculated - i.e, when to run the `create` function again.\n\nWe added a `console.log` call here just to note when a new value is being calculated.\n\nAnd with those changes, we can try our component again (here is the updated code just in case):\n\n```tsx\nconst ExpensiveComponent: React.FC = (props) => {\n const [list, setList] = React.useState([])\n const [counter, setCounter] = React.useState(0)\n const multiplied = React.useMemo(() => {\n console.log('recalculating multiplied:', list)\n return list.map((i) => (i * 972 + 1000) / 5213).join(', ')\n }, [list])\n\n function addRandom() {\n setList((prev) => [...prev, Math.floor(Math.random() * 10000)])\n }\n\n function increaseCounter() {\n setCounter((prev) => ++prev)\n }\n\n return (\n <div>\n Counter: {counter}\n <br />\n Multiplied: {multiplied}\n <br />\n <button onClick={addRandom}>Add Random</button>\n <button onClick={increaseCounter}>Increase Counter</button>\n </div>\n )\n}\n```\n\nIf you now change the counter by using the \"Increase Counter\" button, you will see our `console.log`\ncall is not being invoked again until we use the other button for \"Add Random\".\n\n## React.useCallback\n\nNow we have the other hook - `useCallback` (read the\n[official docs](https://reactjs.org/docs/hooks-reference.html#usecallback)).\n\nThis works exactly like the `useMemo` hook - except it is for functions instead of variable values.\n\nWe can take our button functions, and wrap each in this hook to make sure our function reference\nonly changes when needed.\n\n```tsx\nconst ExpensiveComponent: React.FC = (props) => {\n const [list, setList] = React.useState([])\n const [counter, setCounter] = React.useState(0)\n const multiplied = React.useMemo(\n () => list.map((i) => (i * 972 + 1000) / 5213).join(', '),\n [list],\n )\n const addRandom = React.useCallback(\n () => setList((prev) => [...prev, Math.floor(Math.random() * 10000)]),\n [setList],\n )\n const increaseCounter = React.useCallback(() => setCounter((prev) => ++prev), [setCounter])\n\n return (\n <div>\n Counter: {counter}\n <br />\n Multiplied: {multiplied}\n <br />\n <button onClick={addRandom}>Add Random</button>\n <button onClick={increaseCounter}>Increase Counter</button>\n </div>\n )\n}\n```\n\nNow both our variables and functions are memoized and will only change reference when their\ndependencies dictate they do.\n\n## Caveats\n\nUsing these hooks don't come without their share of problems.\n\n1. Consider whether this actually improves performance or not in your specific case. If your state\n is pretty regularly changed and these memoizations have to run pretty often, their performance\n increases might be outweighed by the performance costs of actually running the memoization logic.\n\n2. Dependency checking and generating can be expensive. Be careful what you put in the dependency\n lists, and if needed, make some more memoization and map your objects and lists in deterministic\n ways so that they are statically inspectable easily. Also avoid using expensive methods such as\n `JSON.stringify` to create those memoizations or dependencies, as it might also be too expensive\n to be worth the trouble and might make things worse.\n\n## Other things to consider\n\nYou might want to make sure your project uses lint rules that enforce exhaustive dependencies, as\nthey make tracking these things much more easy.\n\nIn some cases, you might want to add ignore comments in very specific places, but it makes it very\nclear that this part is built that way intentionally and prevents more confusion about when to\nupdate the dependencies.\n\n---\n\nHopefully you find this useful. There are many other hooks to learn about, but these 2 are very\nuseful and often ignored, so I thought it would be good to highlight them. If you are interested you\nmight want to look up `useRef` and how it differs from `useMemo`, or maybe I'll make another part\nabout it in the future. Who knows?\n","collection":"post","data":{"title":"How to use React memoization hooks for increased performance","showTitle":true,"image":"/images/post_covers/pexels-pixabay-235922.jpg","includeLowResHero":false,"prose":true,"date":"2022-08-10T13:41:15.000Z","tags":"tutorial development react javascript typescript"}},"content":{"headings":[{"depth":2,"slug":"reactusememo","text":"React.useMemo"},{"depth":2,"slug":"reactusecallback","text":"React.useCallback"},{"depth":2,"slug":"caveats","text":"Caveats"},{"depth":2,"slug":"other-things-to-consider","text":"Other things to consider"}],"remarkPluginFrontmatter":{"title":"How to use React memoization hooks for increased performance","date":"2022-08-10 16:41:15 +0300","tags":"tutorial development react javascript typescript","image":"/images/post_covers/pexels-pixabay-235922.jpg"}}},{"params":{"slug":"2022-05-01-the-5-steps-of-completion-learning-to-break-coding-problems-down"},"props":{"id":"2022-05-01-the-5-steps-of-completion-learning-to-break-coding-problems-down.md","slug":"2022-05-01-the-5-steps-of-completion-learning-to-break-coding-problems-down","body":"\nWhen developers first start their journey, sometimes syntax is easy. You've memorized a few\ncommands, classes, and method names, and you can make simple applications quite easily. Also, there\nis plenty of docs and tutorials online - as well as Stack Overflow - surely copying-and-pasting your\nway through would be enough, right?\n\nLet's try to answer the question: how to start approaching problem solving for any given task?\n\n<!-- more -->\n\nWell, the real problem most **actually** face on a day-to-day basis when trying to program is not\ndetermining which lines of *if*s and _for_ loops go where. The problem starts at... well, the\nproblem. Where to start? How to approach a solution to a given task or problem you are facing?\n\n> Some people, when confronted with a problem, think \"I know, I'll use regular expressions.\" Now\n> they have two problems.\n>\n> ~ Jamie Zawinski\n\n## Learning to put theory to practice\n\nIn my time developing I tried hard to learn to recognize the most important skills a developer has\nin their tool set - and one of the biggest hurdles for starting developers - is the ability to know\n_how_ to face _any_ problem - at least, in the context of development.\n\nWhen an up-coming developer gets a new task &ndash; either from someone else (a boss, a colleague)\nor that they themselves define (for challenge, for proof of concept, for learning) - they often get\nstuck at the first hurdle - \"where do I start?\"\n\nNow that you can create simple sentences &ndash; you have no idea where to begin when trying to\nwrite entire paragraphs or essays. How does it my \"hello world\" actually do what it should? How do I\ntake what I know and combine the parts so they work with each other?\n\nHere, I will try to give you some thought experiments and tools so you can be self-sufficient when\nit comes to starting a new coding task, whether it is adding to existing code or beginning from\nscratch.\n\nWe will start very simple, and then try to apply the same ideas to more complex tasks later on. So\nwhile it may seem very stupid and superficial at first, it will come together soon enough.\n\n## Defining a \"problem\"\n\nA **problem** in our context is any **task** that has been given, however large or small.\n\nFor example, here are some small task examples:\n\n- Add a \"Contact\" button to the home page that links to the contact page [design attached]\n- Create a program that calculates the area of a rectangle\n\nAnd here are some larger tasks:\n\n- Create a snake game\n- Create a to-do list application\n\nAll tasks are completely unique, and yet somewhat alike. How do we approach each of these from a\ndevelopment perspective?\n\n## The methodology - The 5 Steps of Completion\n\nLet's start introducing our frame of thought. We need to define the context of our task, so we can\nask the proper questions right after.\n\n### 1. Ask Questions\n\nLet's take a basic example to start.\n\n**Given story: Add a \"Contact\" button to the home page that links to the contact page**\n\nAsk yourself the following questions:\n\n1. What is the current **state**? (i.e., from what point do I start?) \n This won't become a task later, but will be important for you to picture a \"delta\" of sorts,\n which draws a path in your mind from A (what there is) to B (what you want achieve).\n\n2. What does the user expect to **see** change?\n\n3. What **interactions** can the user make with the change?\n\n### 2. Give Answers\n\nLet's attempt to answer these questions for this case, one by one.\n\n1. **Q:** What is the current state?\n\n **A:** The user can see a website, with various sections, images and links. In the home page,\n there is no button that links to the \"Contact\" page.\n\n2. **Q:** What does the user expect to see change?\n\n **A:** The user should see a new button with the text \"Contact\"\n\n3. **Q:** What interactions can the user make with the change?\n\n **A:** The user should be able to click the button. Clicking it will point to the \"Contact\" page.\n\nWe didn't learn a lot of new information by breaking down these questions. But we did gain\nseparation which is critical. Now we have a **list of tasks**, instead of **one task**.\n\nWhy is that helpful?\n\n### 3. Break it down\n\nLarge and small tasks alike &ndash; even the smallest of addition to your code, can be broken down\neven further. It is crucial that we learn to break things down as developers, because then we can\ncreate **actionable items** which we can start **working** on.\n\nThe example above might be a bit trivial, but it gets the point across - we can break everything\ndown further, into infinity. Really, this has no limit. But we should stop somewhere...\n\nSo what next?\n\n### 4. Create Actionables\n\nNow what we want to do is take the answers from above, and compile them into a list of actions. The\nfirst question is redundant as it describes what is going on right now. So we will list the rest and\ntry to create action items from them:\n\n- **The user should see a new button with the text \"Contact\"**\n 1. Show a button on the page\n 2. Have the text say \"Contact\"\n 3. Design the button to fit with the design\n- **The user should be able to click the button. Clicking it will point to the \"Contact\" page.**\n 1. Attach a click \"handler\" to the button\n 2. Make it redirect the page to the desired URL\n\nWe are making some progress. We now have a list of tasks to work with. See how 2 small questions\nalready turned into 5 tasks, and that is for a tiny problem.\n\n### 5. Solve It\n\nNow that we have tasks - we can get crackin'.\n\nFor the sake of this let's assume we are developers that are relatively new to web development. What\nwould we do to approach this task?\n\n#### When in doubt, look it up\n\nGoogle, Duck Duck Go, Brave, whatever. What do you choose? Your preference. What would we search for\nhere exactly?\n\nThe trick is to be as **broad as possible**, and when the answers don't help, get more specific from\nthere. This is different from debugging - where you should be as specific as possible and search for\nexact matches. But I digress. Oh, and always prepend/append the programming/hypertext language you\nare referencing.\n\nThe action item is \"Show a button on the page\". So let's try see: **how do we display a button in\nHTML?** and **how do I set its text?** Now that we have these simple questions, we can remove some\nexcess words and we remain with **2 great keywords** to search which should get us starting on the\ntopic, at the most general sense:\n\n- [html button](https://google.com/search?q=html+button)\n\nNow to answer the next burning question - **how to make the button fit with the design?**\n\nWe have another tricky task here. \nSay our design looks something like this:\n\n![button example](/images/breakdown-problems/button-example.png)\n\nHere, if we are unfamiliar with everything, breaking down even further can really help. Let's look\nat what defines the style of this button:\n\n- It has a gradient fill/background\n- It has white text\n- It has rounded corners\n- It has a shadow behind it\n\nWe will want to search for more specific things, which will also be our broken down tasks; such as:\n\n- [css gradient fill](https://google.com/search?q=css+gradient+fill)\n- [css text color](https://google.com/search?q=css+rounded+box)\n- [css rounded box](https://google.com/search?q=css+rounded+box)\n- [css shadow](https://google.com/search?q=css+shadow)\n\nTry some more simple task ideas, and practice breaking down tasks into chunks that you can\ndefinitely complete. As tasks become more complex, they will sometimes become harder. But sometimes\nyou will find that they don't, really &mdash; they just become **longer**, and **larger**, which may\nseem daunting, but all big tasks can sometimes be broken up into many, many tiny tasks, which will\nbe easy to actually get done.\n\n#### Any progress is important: No task is too small\n\nOne of the important concepts behind this approach is that tasks should be broken down until you can\npicture in your mind the entire task's execution, to some extent. It's not really a rule to follow,\nbut more of a guiding line. Tasks that you can picture yourself doing almost fully, you can probably\ncomplete. And if the smallest task is still beyond your knowledge, then you try to break it down\nfurther, or must go out and seek that knowledge.\n\nWhich is to say - in order to make your tasks **useful**, you must make them **doable**. Once you\ncan tick a checkbox that says \"I'm done\", no matter the size of the contribution to the total pool\nof tasks, you still made some **progress**. That progress piles up slowly, and soon you will see\nyour projects come to life.\n\n#### The next steps\n\nPractice makes perfect - so whatever side project you have, apply this new knowledge to it. Take\nyour tasks, ask the questions, and turn them into smaller ones.\n\nIn the next part of this series, we will take a look at some more complex examples - abstract, large\nideas that need to become a coded reality. For the meantime, a good grasp of simple tasks breakdown\nwill shape your minds properly for the next difficulty that lies ahead.\n","collection":"post","data":{"title":"The 5 Steps of Completion: Learning to break coding problems down","showTitle":true,"image":"/images/post_covers/pexels-mentatdgt-2173508.jpg","includeLowResHero":false,"prose":true,"date":"2022-05-01T17:00:00.000Z","tags":"development"}},"content":{"headings":[{"depth":2,"slug":"learning-to-put-theory-to-practice","text":"Learning to put theory to practice"},{"depth":2,"slug":"defining-a-problem","text":"Defining a “problem”"},{"depth":2,"slug":"the-methodology---the-5-steps-of-completion","text":"The methodology - The 5 Steps of Completion"},{"depth":3,"slug":"1-ask-questions","text":"1. Ask Questions"},{"depth":3,"slug":"2-give-answers","text":"2. Give Answers"},{"depth":3,"slug":"3-break-it-down","text":"3. Break it down"},{"depth":3,"slug":"4-create-actionables","text":"4. Create Actionables"},{"depth":3,"slug":"5-solve-it","text":"5. Solve It"},{"depth":4,"slug":"when-in-doubt-look-it-up","text":"When in doubt, look it up"},{"depth":4,"slug":"any-progress-is-important-no-task-is-too-small","text":"Any progress is important: No task is too small"},{"depth":4,"slug":"the-next-steps","text":"The next steps"}],"remarkPluginFrontmatter":{"title":"The 5 Steps of Completion: Learning to break coding problems down","date":"2022-05-01 20:00:00 +0300","categories":"tutorials development","tags":"development","image":"/images/post_covers/pexels-mentatdgt-2173508.jpg"}}},{"params":{"slug":"2022-04-21-placeholder-photo"},"props":{"id":"2022-04-21-placeholder-photo.md","slug":"2022-04-21-placeholder-photo","body":"\nWhen prototyping UI designs or code, we often need images to put in places when we have no finalized\nimages in the design, or in order to test out different sizes and colors when only some are\nprovided.\n\nI find myself needing to find an image to use when developing websites and apps often, and finding a\nmock image to upload is time consuming - not to mention leaves possibly unwanted files in your code\nor design.\n\nSo what do we do?\n\nEnter: [placeholder.photo][1].\n\n<!-- more -->\n\n## What is placeholder.photo?\n\n[Placeholder.photo][1] is a service that lets you use placeholder images anywhere, saving you the\nhassle of creating images in the required sizes for temporary use-cases.\n\nIt is a tool that gives you an image URL to use anywhere, from any web or other origin.\n\n## Why would I need this?\n\nSay you are working on implementing a website design in HTML/CSS and more languages of your\nchoosing.\n\nWhen confronted with the need to use an image, for, say, an image that exists on the design -\nusually I would put it in the appropriate asset location in the project.\n\nBut what if any of these is true...\n\n1. The image represents **user uploaded content**, so the size is variable?\n2. The image is not finalized and ready for use in the design?\n3. The image does not exist at all and a placeholder box is given in the design docs?\n\n## How does placeholder.photo help me solve this?\n\nNow instead of having to go ahead and either find images of appropriate sizes, making sure they are\nuploaded to an accessible location online, or in the case of updates to the spec, now having to\nupdate said images...\n\nYou can just take a URL from [placeholder.photo][1].\n\n<div class=\"img center img-sz-none\" markdown=\"1\">\n\n![The placeholder.photo custom tool](/images/placeholder-photo/placeholder-photo-tool-preview.png)\n\n</div>\n\nThe preview tool lets you decide how the image looks by letting you customize size, background and\ntext colors, the text content and font size.\n\nBut really, you can just customize the URL yourself and use the images as necessary without having\nto rely on it.\n\nA URL like this:\n\n```text\nhttps://placeholder.photo/img/255?bg_color=fb9e00&fg_color=ffffff&text=My+Text\n```\n\nWill generate this image:\n\n<div class=\"img center img-sz-none\" markdown=\"1\">\n\n![Preview](https://placeholder.photo/img/255?bg_color=fb9e00&fg_color=ffffff&text=My+Text)\n\n</div>\n\nWhen generating from the tool, you can easily copy the image URL you made using the copy button:\n\n<div class=\"img center img-sz-none\" markdown=\"1\">\n\n![Copy button example](/images/placeholder-photo/placeholder-photo-copy-preview.png)\n\n</div>\n\nOr drag & drop to a supporting tool:\n\n<div class=\"img center img-sz-md\" markdown=\"1\">\n\n![Drag and drop example](/images/placeholder-photo/placeholder-photo-drag-n-drop-preview.gif)\n\n</div>\n\nAnd that's it! Enjoy easier time coding & designing.\n\n<div class=\"center\" markdown=\"1\">\n\n![Thank](https://placeholder.photo/img/100x48?bg_color=68bc00&fg_color=ffffff&text=Thank&font_size=20)\n![you](https://placeholder.photo/img/100x48?bg_color=7b64ff&fg_color=ffffff&text=You%21&font_size=20)\n\n![casraf.dev](https://placeholder.photo/img/208x48?bg_color=d33115&fg_color=ffffff&text=casraf.dev&font_size=20)\n\n</div>\n\n[1]: https://placeholder.photo\n","collection":"post","data":{"title":"Placeholder.photo: An easy way to add mockup images to your UI and web development workflow","showTitle":true,"image":"/images/post_covers/pexels-pixabay-459653.jpg","includeLowResHero":false,"prose":true,"date":"2022-04-21T12:55:03.000Z","tags":"project development tool"}},"content":{"headings":[{"depth":2,"slug":"what-is-placeholderphoto","text":"What is placeholder.photo?"},{"depth":2,"slug":"why-would-i-need-this","text":"Why would I need this?"},{"depth":2,"slug":"how-does-placeholderphoto-help-me-solve-this","text":"How does placeholder.photo help me solve this?"}],"remarkPluginFrontmatter":{"title":"Placeholder.photo: An easy way to add mockup images to your UI and web development workflow","date":"2022-04-21 15:55:03 +0300","excerpt_separator":"<!-- more -->","tags":"project development tool","image":"/images/post_covers/pexels-pixabay-459653.jpg"}}},{"params":{"slug":"2019-03-22-flutter-tutorial-creating-a-wheel-spinner-widget"},"props":{"id":"2019-03-22-flutter-tutorial-creating-a-wheel-spinner-widget.md","slug":"2019-03-22-flutter-tutorial-creating-a-wheel-spinner-widget","body":"\n[Flutter][flutter] is an amazing framework for app development, and gaining popularity every second.\nBeing component and composition based, and with the fact that it includes many built-in widgets to\nwork with, it's really easy to create beautiful widgets, either simple or complex.\n\nToday we'll be learning how to create a real-world case widget for updating a numeric value - a\npitch-knob-like control. Like the ones you can usually find in a studio. It will also support\nletting the finger go to keep rolling in the same direction, and using the velocity it was left\nwith.\n\n<!-- more -->\n\n### In this tutorial, we will:\n\n1. Create a stateful widget\n2. Use `GestureDetector` to detect scroll gestures for us, and update the callbacks appropriately.\n\n**This tutorial assumes you have some experience with Flutter and understand basic Widget\ncomposition and basic state management.**\n\n## Step 1: Create the Widget\n\nWe'll start basic. We have one base stateful widget, which will hold everything. In this example,\nI've already added some parameters we will need to accept:\n\n```dart\nclass WheelSpinner extends StatefulWidget {\n final double max;\n final double min;\n final double value;\n final Function(double value)? onSlideUpdate;\n final Function(double value)? onSlideDone;\n\n WheelSpinner({\n required this.value,\n this.max = double.infinity,\n this.min = double.negativeInfinity,\n this.onSlideDone,\n this.onSlideUpdate,\n });\n\n @override\n _WheelSpinnerState createState() => _WheelSpinnerState();\n}\n\nclass _WheelSpinnerState extends State<WheelSpinner> {\n late double value;\n\n @override\n void initState() {\n value = widget.value;\n super.initState();\n }\n\n @override\n Widget build(BuildContext context) {\n return Container(child: Text(value.toString()));\n }\n}\n```\n\nAs you can see, we are accepting a `min`, `max`, and current `value` to use with the widget. We are\nalso accepting `callback`s for when updating (while sliding) and when done (when finger is let go\nand the \"fling\" duration is over), which we will call once we update the actual value.\n\n### Let's give it some shape\n\nRight now our widget is completely empty, except for that little `Text` widget to see our current\nvalue, so let's do some styling. For this example, We will be creating a rounded, tall box, which\nwill contain some separation lines, and maybe some shade or gradient.\n\nLet's update our build method:\n\n```dart\n@override\nWidget build(BuildContext context) {\n double shadowOffset = 0.2;\n return Container(\n width: 60,\n height: 100,\n decoration: BoxDecoration(\n gradient: LinearGradient(\n begin: Alignment.topCenter,\n end: Alignment.bottomCenter,\n stops: [0.0, shadowOffset, 1.0 - shadowOffset, 1.0],\n colors: [\n Colors.grey[350],\n Colors.grey[50],\n Colors.grey[50],\n Colors.grey[350]\n ],\n ),\n border: Border.all(\n width: 1,\n style: BorderStyle.solid,\n color: Colors.grey[600],\n ),\n borderRadius: BorderRadius.circular(3.5),\n ),\n );\n}\n```\n\nWe should see something like this:\n\n<div class=\"max-w-sm mx-auto\">\n\n![Widget - without lines](/images/wheel-spinner-tutorial/scr01.png)\n\n</div>\n\nStill no divider lines, though. Let's say we want to divvy up our box to 10 segments, so that each\ntime one of them goes outside the bounding box, we increase or decrease the value by 1. In this\nexample we create a `Stack`, with 11 lines (10 for each division + 1 extra for the scroll effect)\ngoing from top to bottom (note the `lineTopPos` function that gets the correct `y` value):\n\n```dart\n@override\nWidget build(BuildContext context) {\n // ...\n decoration: // ...,\n child: Container(\n child: Stack(\n children: List<Widget>.generate(\n 11,\n (i) {\n var top = lineTopPos(value, i);\n return Positioned.fromRect(\n rect: Rect.fromLTWH(0.0, top, 60, 0),\n child: Divider(\n color: Colors.grey[600],\n ),\n );\n },\n ).toList(),\n ),\n ),\n\n // ...\n}\n\ndouble lineTopPos(double value, int i) {\n double valueFraction = (value.ceil() - value) * 10.0;\n double indexedTop = 10.0 * i;\n double top = indexedTop + valueFraction;\n return top;\n}\n```\n\nNote the line that sets `valueFraction`. We take our `value.ceil()` and reduce the current value.\nThis always gives us a number between `0.0` and `1.0` that tells us how much of the _next_ segment\nto show. In reality, whenever we update `value`, we will always consider the small fraction we are\nscrolling into, which means we don't jump by 1 every time, which will cause the lines to represent\nthe value correctly, and also smoothly move as we input our scroll.\n\nNow, we have something like this:\n\n<div class=\"max-w-sm mx-auto\">\n\n![Widget - with lines](/images/wheel-spinner-tutorial/scr02.png)\n\n</div>\n\nAnd now that it's all nice and pretty, let's start handling the logic.\n\n## Step 2 - Detecting gestures and updating the value\n\nWe can now wrap our widget with a `GestureDetector`. This is a built-in widget that lets you capture\nand use scroll, tap and multi-tap gestures on the child widget, and its decendants (that last part\ndepends on the `behavior` parameter).\n\n```dart\n//...\nGestureDetector(\n onVerticalDragStart: onDragStart,\n onVerticalDragUpdate: onDragUpdate,\n onVerticalDragEnd: onDragDone,\n child: /* our widget */\n),\n//...\n```\n\nAnd of course, we need to actually define `onDragStart`, `onDragUpdate` and `onDragDone`.\n\n#### 1. onDragStart\n\nWe'll start by capturing on what `value` and position the finger first started dragging. For that,\nwe will save them in our state:\n\n```dart\nOffset dragStartOffset;\ndouble dragStartValue;\n\n// ...\n\nvoid onDragStart(DragStartDetails details) {\n setState(() {\n dragStartOffset = details.globalPosition;\n dragStartValue = value;\n });\n}\n```\n\n<div></div>\n\n#### 2. onDragUpdate\n\nOn every update, aka when the finger slides up and down, we want to take the distance between the\noriginal start point, and use that to calculate our new value. If the finger scrolled up an amount\nequivalent to 10 separator lines, we increase/decrease by 10 accordingly. Of course, these numbers\nwill be much smaller since we are updating a double, on a subpixel basis.\n\n```dart\nvoid onDragUpdate(DragUpdateDetails details) {\n var newValue = clamp(\n dragStartValue - (details.globalPosition - dragStartOffset).dy / 20.0,\n widget.min,\n widget.max);\n setState(() {\n value = newValue;\n });\n if (widget.onSlideUpdate != null) {\n widget.onSlideUpdate(value);\n }\n}\n```\n\nWe set the new value to use the `dragStartValue` and decrease by the distance of the scroll so far,\ndivided by 20 to scale appropriately with the separator lines. Then we update using the callback, if\nthat's relevant.\n\n_Note:_ the `clamp` method is a just a convenience method to keep a number between 2 boundaries.\nHere is a basic implementation:\n\n```dart\ndouble clamp<T extends num>(T number, T low, T high) =>\n max(low * 1.0, min(number * 1.0, high * 1.0));\n```\n\n#### 3. Testing the current widget\n\nWe can already test out our widget - we still can't \"fling\" the finger, but we can drag our finger\nup and down to see the value updating.\n\nLet's add a Text widget to our parent `build` method, to see the value from the state that's calling\nit. Here is an example of calling our widget:\n\n```dart\nclass _MyHomePageState extends State<MyHomePage> {\n //...\n @override\n Widget build(BuildContext context) {\n return Scaffold(\n appBar: AppBar(\n title: Text(widget.title),\n ),\n body: Center(\n child: Column(\n mainAxisSize: MainAxisSize.min,\n children: <Widget>[\n WheelSpinner(\n value: _counter.toDouble(),\n min: 0.0,\n max: 100.0,\n onSlideUpdate: (val) => setState(() {\n _counter = val;\n }),\n ),\n Padding(\n padding: const EdgeInsets.only(top: 16.0),\n child: Text(_counter.toStringAsFixed(2), textScaleFactor: 2.0,),\n ),\n ],\n ),\n ),\n );\n }\n // ...\n}\n```\n\nNow it should work and look like this:\n\n<div class=\"max-w-sm mx-auto\">\n\n![App example - animation without fling](/images/wheel-spinner-tutorial/scr04.gif)\n\n</div>\n\n#### 4. onDragDone\n\nOur last piece is also the most fun. Here we will start handling our \"fling\" physics. We need 2\nthings here:\n\n1. Use the velocity of the letting-go gesture to figure out how much to add/reduce from the value\n2. Dampen this value slowly to create an eased roll effect\n\nLuckily, animations are super useful in a case like this, for applying curves to a path between 2\nnumber values - we don't need to calculate the damping ourselves.\n\nSo first thing's first, we need to define and create a new `AnimationController`, and\n`Animation<double>`, and let's also set their initial value in our `initState` method:\n\n```dart\nAnimationController flingController;\nAnimation<double> flingAnimation;\nvoid Function() currentFlingListener;\n\n@override\ninitState() {\n flingAnimation = AlwaysStoppedAnimation(0.0);\n flingController = AnimationController(vsync: this);\n // ...\n}\n```\n\nAlso, since we use animations now, we will want to mixin `SingleTickerProviderStateMixin`, which\nwill manage a ticker for us, for the animation to use:\n\n```dart\nclass _WheelSpinnerState extends State<WheelSpinner>\n with SingleTickerProviderStateMixin {\n // ...\n}\n```\n\nThen, we can start with our new method:\n\n```dart\nvoid onDragDone(DragEndDetails details) {\n setState(() {\n dragStartOffset = null;\n });\n double velocity = details.primaryVelocity;\n if (velocity.abs() == 0) {\n if (widget.onSlideDone != null) {\n widget.onSlideDone(value);\n }\n return;\n }\n}\n```\n\nIn the above lines we simply reset the drag start offset, as it's no longer relevant, now that the\nfinger was let go. Then we get the velocity of the drag, and if it's 0, we return early and submit\nour callback, `onSlideDone` with the latest value.\n\nNow, we can proceed with handling the \"fling\".\n\nWe'll start by saving the value that was set when we first let go of the finger.\n\n```dart\nvoid onDragDone(DragEndDetails details) {\n // ...\n double originalValue = value;\n // ...\n}\n```\n\nNow, we want to add a listener to our animation value. We will generate a listener based on the\nvalue that was let go at, because we will need it to calculate the updated value:\n\n```dart\nflingListener(double originalValue) {\n return () {\n double newValue =\n clamp(originalValue - flingAnimation.value, widget.min, widget.max);\n if (newValue != value) {\n setState(() {\n value = newValue;\n });\n if (flingAnimation.value == flingController.upperBound) {\n if (widget.onSlideDone != null) {\n widget.onSlideDone(value);\n }\n } else {\n if (widget.onSlideUpdate != null) {\n widget.onSlideUpdate(value);\n }\n }\n }\n };\n}\n```\n\nIn the line:\n\n```dart\ndouble newValue =\n clamp(originalValue - flingAnimation.value, widget.min, widget.max);\n```\n\nYou can see we use the animation value and simply decrease it from the original value. Depending on\nthe fling direction, this will either continue up or down along with the animation value.\n\nNow we can set our listener to that function once we call it. Saving it to an instance variable will\nallow us to remove the listener on dispose, as we will have a reference to the same listener\nfunction.\n\n```dart\n // ...\n currentFlingListener = flingListener(originalValue);\n // ...\n```\n\nThen we start a `Tween` animation: we start at `0.0`, and end at `velocity`. Whether the velocity is\npositive or negative, the math will work to reach the final number we want.\n\nWe set the `curve` as we want (in this case, `Curves.decelerate`), and attach the `parent` animation\ncontroller; attach the listener, and finally when we're done, we can use `forward()` to start\nanimating.\n\n```dart\n flingController.duration = Duration(milliseconds: velocity.abs().toInt());\n flingAnimation =\n Tween(begin: 0.0, end: velocity / 100).animate(CurvedAnimation(\n curve: Curves.decelerate,\n parent: flingController,\n ))\n ..addListener(currentFlingListener);\n flingController\n ..reset()\n ..forward();\n}\n```\n\nWe added `reset()` just before `forward()`, to make sure no previous animations are lingering for\nsome odd reason. In fact, let's add a `stop()` calls to `onDragStart` and `onDragUpdate`, as well,\nand also reset the animation itself:\n\n```dart\nvoid onDragStart(DragStartDetails details) {\n flingController.stop();\n flingAnimation = AlwaysStoppedAnimation(0.0);\n // ...\n}\n```\n\n```dart\nvoid onDragUpdate(DragUpdateDetails details) {\n flingController.stop();\n flingAnimation = AlwaysStoppedAnimation(0.0);\n // ...\n}\n```\n\nAnd now that it's all out of the way, our widget should be fully working:\n\n<div class=\"max-w-sm mx-auto\">\n\n![App example - animation with fling](/images/wheel-spinner-tutorial/scr05.gif)\n\n</div>\n\n### Done!\n\nThis should be pretty much it! You can of course style it and expand on it, but I've already made\nthis a package, so any improvements you have, or if you just want to use it, head over to [the\npackage on Dart Pub][pub-package], or feel free to contribute at [the source on GitHub][gh-package].\n\nThe source of the example app used for this tutorial [right here][gh-tut], if you want to take a\nlook and compare.\n\nFeel free to ask questions, provide feedback or correct my mistakes in the comments - I'm sure there\nare some.\n\n[flutter]: https://flutter.dev\n[gh-package]: https://github.com/chenasraf/wheel_spinner\n[pub-package]: https://pub.dartlang.org/packages/wheel_spinner\n[gh-tut]: https://github.com/chenasraf/wheel_spinner_tutorial\n","collection":"post","data":{"title":"Flutter Tutorial - Creating a Wheel Spinner Widget","showTitle":true,"image":"/images/post_covers/pexels-plann-4549416.jpg","includeLowResHero":false,"prose":true,"date":"2019-03-22T10:00:00.000Z","tags":"tutorial flutter dart widget development project"}},"content":{"headings":[{"depth":3,"slug":"in-this-tutorial-we-will","text":"In this tutorial, we will:"},{"depth":2,"slug":"step-1-create-the-widget","text":"Step 1: Create the Widget"},{"depth":3,"slug":"lets-give-it-some-shape","text":"Let’s give it some shape"},{"depth":2,"slug":"step-2---detecting-gestures-and-updating-the-value","text":"Step 2 - Detecting gestures and updating the value"},{"depth":4,"slug":"1-ondragstart","text":"1. onDragStart"},{"depth":4,"slug":"2-ondragupdate","text":"2. onDragUpdate"},{"depth":4,"slug":"3-testing-the-current-widget","text":"3. Testing the current widget"},{"depth":4,"slug":"4-ondragdone","text":"4. onDragDone"},{"depth":3,"slug":"done","text":"Done!"}],"remarkPluginFrontmatter":{"title":"Flutter Tutorial - Creating a Wheel Spinner Widget","date":"2019-03-22 12:00:00 +0200","excerpt_separator":"<!-- more -->","categories":"tutorials flutter development","tags":"tutorial flutter dart widget development project","image":"/images/post_covers/pexels-plann-4549416.jpg"}}},{"params":{"slug":"2019-03-06-simple-scaffold"},"props":{"id":"2019-03-06-simple-scaffold.md","slug":"2019-03-06-simple-scaffold","body":"\nAs most people who have been working a lot in front-end development, I will acknowledge that no\nmatter what framework, library set or tools you work with, you end up replicating a lot of files,\nespecially with modern component-based frameworks, such as React, Angular and Vue.js.\n\nThey usually grow to be very different, but they almost always start with the same some sort of\nbase-skeleton, which gets _built-upon_ later. A lot of pipeline code written, and **time wasted**.\n\n<!-- more -->\n\n## What have we got?\n\nThere are some existing solutions to these problems, but they usually are not very flexible, or are\ntied to one library or implementation. Let's take a look at a few examples.\n\n- _IDE snippets_, such as VS Code's easy snippet extensions/configurations are very helpful.\n Basically, you can either use configuration JSON files to add snippets to your code quickly, or\n use extensions that simply fill these up for you.\n\n There are nice benefits to this, as you can start typing the 'prefix' of the snippet and hit\n <kbd>Tab</kbd> quickly to add small snippets of code. You can also predefine stopping points,\n which you can navigate using <kbd>Tab</kbd> after inserting the snippet, and insert arguments,\n method names, etc.\n\n These are nice, but only for small snippets. When you have to create an entire component, made by\n multiple files, you're stuck with the option to create multiple files yourself, name them\n appropriately, and paste different snippets on each file.\n\n- _Some NPM libraries_ provide ways to scaffold files based on templates, but from what I've seen\n\n - they are tied to specific task runners, libraries or frameworks\n - they are difficult to set up and customize\n - combining your entire stack will be difficult\n\n## Enter: Simple Scaffold\n\n[Read the Simple Scaffold documentation][simple-scaffold]\n\nI was frustrated with this, and all I wanted was to have different sets of files I could generate,\nsimply by copying them at predefined directory structures, and fill them with some variable data.\n\nFor instance: I create a lot of React components at work. We have recently moved to a single-file\ncomponent structure for starting components; but we have different types of components that we want\nto generate based on context, like different file contents for page containers versus general-use\ncomponents.\n\nBeing fed up with my options, I made this small NPM package, that does just this, in a very easy and\nquick way to setup. Simply put your files wherever you want, and either use the CLI tool or the\npackage as an import - and you're good to go.\n\n## Example: Jekyll Posts\n\nCreating Jekyll posts is very simple! However I still had a little bit missing - I have to manually\nadd the date every time? Oh, man. What about the file identifier name, and title? My\n`excerpt_separator`?\n\nSo I just made the simplest script in Ruby (to fit in with the Jekyll theme) to run the Simple\nScaffold CLI with some custom parameters.\n\n```ruby\n#!/usr/bin/env ruby\nrequire \"json\"\n\nSCAFFOLD_DIR = \"scaffold_templates\"\n*name = ARGV\n\ndata = { dateFmt: \"yyyy-MM-dd\", fullDateFmt: \"yyyy-MM-dd HH:mm:ss XX\" }\n\nputs `\n npx simple-scaffold@latest \"#{name.join(\" \")}\" \\\n --templates #{SCAFFOLD_DIR}/**/* \\\n --output _drafts \\\n -w true \\\n -s false \\\n -d '#{JSON.dump(data)}'\n`\nputs 'Done'\n```\n\nLet's run by this real quick:\n\n- _Lines 1-7_ - setting up locals such as template directory base, and some variables to pass to the\n templates.\n- _Lines 9-16_ - We pass the parameters via a shell call (back-ticks in ruby), and immediately run\n using npx.\n\nLocals are passed to [Handlebars][handlebars], and can be used both in file/directory names and file\ncontents.\n\nNow all I had to do is create a place for my templates, and add a template inside:\n\n```text\n- scaffold_templates/\n -- {{now dateFmt}}-{{kebabCase name}}.md\n```\n\nAnd fill it up with some basic post:\n\n```yaml\n---\nlayout: post\ntitle: '{{ startCase name }}'\ndate: { { now fullDateFmt } }\nexcerpt_separator: <!-- more -->\ncategories:\n---\nPost content goes here\n```\n\nAnd voila! Running the script, along with a name:\n\n```shell\n./scaffold.rb \"Billy Jean is not my lover\"\n```\n\nGenerates the following file structure:\n\n```text\n- _posts/\n -- 2019-03-06-billy-jean-is-not-my-lover.markdown\n```\n\nWith the following content:\n\n```yaml\n---\nlayout: post\ntitle: 'Billy Jean is not my lover'\ndate: 2019-03-06 16:43:38 +0200\nexcerpt_separator: <!-- more -->\ncategories:\n---\nPost content goes here\n```\n\n## You can do more\n\nTake the same approach and think what you could create generators for with 0 effort. React or Vue\ncomponents? You can bundle each in a folder with all the imports, exports, base props, etc all lined\nup with your component name.\n\nAdd a script to your `package.json`, a couple of files, and you're good to go:\n\n**package.json**\n\n```json\n{\n \"gen:component\": \"npx simple-scaffold@latest -t scaffold_templates/react-component -o src/components -s true -w true '{\\\"className\\\": \\\"myClassName\\\",\\\"author\\\": \\\"Chen Asraf\\\"}'\"\n}\n```\n\n**`scaffold_templates/react-component/{{pascalCase name}}.tsx`**\n\n```tsx\n/**\n * Author: {{ author }}\n * Date: {{ now \"yyyy-MM-dd\" }}\n */\nimport React from 'react'\nimport { ComponentProps } from 'utils/types'\n\nexport interface {{pascalCase name}}Props extends ComponentProps {\n className?: string\n}\n\nexport default {{camelCase name}}: React.FC<{{pascalCase name}}Props> = (props) => {\n return (\n <div className=\"{{className}}\">{{camelCase name}} Component</div>\n )\n}\n```\n\n**`scaffold_templates/react-component/index.ts`**\n\n```tsx\nexport * from './{{pascalCase name}}'\n```\n\nRunning `npm run gen:component MyComponent` will quickly generate your new components:\n\n**`src/components/MyComponent/MyComponent.tsx`**\n\n```tsx\n/**\n * Author: Chen Asraf\n * Date: 2022-08-10\n */\nimport React from 'react'\nimport { ComponentProps } from 'utils/types'\n\nexport interface MyComponentProps extends ComponentProps {\n className?: string\n}\n\nexport default MyComponent: React.FC<MyComponentProps> = (props) => {\n return (\n <div className=\"myClassName\">MyComponent Component</div>\n )\n}\n```\n\n**`src/components/MyComponent/index.ts`**\n\n```tsx\nexport * from './MyComponent'\n```\n\nWhy stop there? **Create entire app boilerplates** using the same method - create your boilerplate\napp, add all your libraries, and replace your app name with `{{name}}`. That's it! You can run this\npackage with input from any local files and output them in (or as) your next project.\n\nCheck out the documentation by clicking [this link][simple-scaffold]!\n\n[handlebars]: https://handlebarsjs.com\n[simple-scaffold]: https://casraf.dev/simple-scaffold\n","collection":"post","data":{"title":"Simple Scaffold: Generating multiple files for faster coding","showTitle":true,"image":"/images/post_covers/pexels-ivan-samkov-4491918.jpg","includeLowResHero":false,"prose":true,"date":"2019-03-06T13:43:40.000Z","tags":"project node typescript javascript development tool"}},"content":{"headings":[{"depth":2,"slug":"what-have-we-got","text":"What have we got?"},{"depth":2,"slug":"enter-simple-scaffold","text":"Enter: Simple Scaffold"},{"depth":2,"slug":"example-jekyll-posts","text":"Example: Jekyll Posts"},{"depth":2,"slug":"you-can-do-more","text":"You can do more"}],"remarkPluginFrontmatter":{"title":"Simple Scaffold: Generating multiple files for faster coding","date":"2019-03-06 15:43:40 +0200","excerpt_separator":"<!-- more -->","tags":"project node typescript javascript development tool","image":"/images/post_covers/pexels-ivan-samkov-4491918.jpg"}}}]