Update from obsidian - thinkpad
Affected files: .obsidian/app.json .obsidian/graph.json .obsidian/plugins/obsidian-omnivore/data.json .obsidian/plugins/update-time-on-edit/data.json 01. Projects/Fuuka/Outline.md 01. Projects/Juuno/Outline.md 01. Projects/Renuncia/Renuncia.md 02. Areas/Dotfiles/dotfiles tasks.md 02. Areas/Escape Latam/Canada.md 02. Areas/Escape Latam/Comparación de Paises.md 02. Areas/Escape Latam/Escapar de Latam.md 02. Areas/Escape Latam/New Zeldand.md 03. Resources/Development/Feature shipment checklist.md 03. Resources/Development/Fix messy commits.md 03. Resources/Development/Git.md 03. Resources/Development/Iframes.md 03. Resources/Development/Revert old changes in a codebase.md 03. Resources/Development/Search for a bug.md 03. Resources/Notetaking/Habits.md Read Later/2024-02-12 - Debouncing in JavaScript – Explained by Building Auto-Complete Functionality in React.md notes/Git.md
This commit is contained in:
parent
c82f170ec4
commit
c84ad03d4c
21 changed files with 573 additions and 61 deletions
2
.obsidian/app.json
vendored
2
.obsidian/app.json
vendored
|
|
@ -3,7 +3,7 @@
|
|||
"showLineNumber": true,
|
||||
"tabSize": 2,
|
||||
"vimMode": true,
|
||||
"newFileLocation": "folder",
|
||||
"newFileLocation": "current",
|
||||
"alwaysUpdateLinks": true,
|
||||
"useMarkdownLinks": true,
|
||||
"showUnsupportedFiles": true,
|
||||
|
|
|
|||
2
.obsidian/graph.json
vendored
2
.obsidian/graph.json
vendored
|
|
@ -17,6 +17,6 @@
|
|||
"repelStrength": 10,
|
||||
"linkStrength": 1,
|
||||
"linkDistance": 250,
|
||||
"scale": 0.4952113402093565,
|
||||
"scale": 0.3580284794893512,
|
||||
"close": true
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
"dateSavedFormat": "yyyy-MM-dd HH:mm:ss",
|
||||
"apiKey": "ec3bba50-4770-471b-99b1-9953ca523d8c",
|
||||
"filter": "ADVANCED",
|
||||
"syncAt": "2024-02-17T17:22:18",
|
||||
"syncAt": "2024-02-20T12:01:36",
|
||||
"customQuery": "in:archive has:highlights",
|
||||
"template": "# {{{title}}}\n\n{{# note }}\n## Notes\n\n{{{ note }}}\n{{/ note }}\n{{#highlights.length}}\n## Highlights\n\n{{#highlights}}\n{{{text}}} \n{{#note}}\n\n> [!note]\n> {{{note}}}\n{{/note}}\n\n[source]({{{highlightUrl}}}) {{#labels}} #{{name}} {{/labels}}\n\n---\n\n{{/highlights}}\n{{/highlights.length}}\n## Original\n\n{{{ content }}}",
|
||||
"highlightOrder": "LOCATION",
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
"version": "1.6.3",
|
||||
"isSingleFile": false,
|
||||
"frequency": 60,
|
||||
"intervalId": 13,
|
||||
"intervalId": 14,
|
||||
"frontMatterVariables": [],
|
||||
"frontMatterTemplate": "id: {{{ id }}}\ntitle: >\n {{{ title }}}\nstatus: {{{ state }}}\ntags:\n - read-later\n{{#labels.length}}\n{{#labels}} - {{{name}}}\n{{/labels}}\n{{/labels.length}}\ndate_added: {{{ dateSaved}}}\nurl_omnivore: >\n {{{ omnivoreUrl}}}\nurl_original: >\n {{{ originalUrl}}}"
|
||||
}
|
||||
26
.obsidian/plugins/update-time-on-edit/data.json
vendored
26
.obsidian/plugins/update-time-on-edit/data.json
vendored
|
|
@ -18,21 +18,16 @@
|
|||
"notes/testo.md": "24df1da31f19afae3f434032b447e804a8bcbdeeb1b270c6d04f28bfba5583fb",
|
||||
"notes/North Start.md": "662704939ce34782d6578cd8d91d5d7d738de19ce5eb34196f71733744bc1286",
|
||||
"README.md": "10ebf3cf75edbc6a6ccaf5c000185cde1e70bdc86c13d5b150d06546bad9c4dc",
|
||||
"01. Projects/Escape Latam/Comparación de Paises.md": "6fb41dde50e73d696948be100340f951fc490f40cbcdda45584719fd7ba57194",
|
||||
"notes/Devlog.md": "8796c41169b072acea9ef97211e294ba7b464ccffabc1259c7763440fea7a63e",
|
||||
"01. Projects/Obtener pasaporte/Obtener pasaporte.md": "f3c1769ca0f40f87c41954c48b4cc791cf77dc22422e15a7005a880d9b030de9",
|
||||
"01. Projects/Escape Latam/Escapar de Latam.md": "4ac856a52471f5eb6923790a96bbbf9daa867137cf9b8add09f498ba87e4469e",
|
||||
"01. Projects/Escape Latam/New Zeldand.md": "c46e7d6cb08da2a7d5500640abc560f5db37dd056561420bce56df1c6eb485c7",
|
||||
"01. Projects/Escape Latam/Canada.md": "974bdb92354ff2d46e67f524390bce477c8b0f753012f4f86eb0d4291b10a6ce",
|
||||
"02. Areas/Dotfiles/dotfiles tasks.md": "bc454043d61c51de530e28dafe5b296077159e19b8c492f6d7de0d455b28bf32",
|
||||
"01. Projects/Renuncia/Renuncia.md": "17d174d61ae84b4f1393490e5ea89832cc28b1aa7a53ec78016ea30a1fccdcd2",
|
||||
"02. Areas/Dotfiles/dotfiles tasks.md": "9b662ba9122aaff0817522108a3a2dce6ee203b7f2c58abc8f4934e3899d878a",
|
||||
"01. Projects/Renuncia/Renuncia.md": "088263e8c8b8719835e203690683861cd889b1c2a1bf17bdfa09243b8eadbe53",
|
||||
"01. Projects/Página Personal/board.md": "5ccecc6fd345d2e5361e6a55837716753426acbb020e4adcce89fc71c7d0b812",
|
||||
"02. Areas/Food/recipes.md": "320958ea965e90d56fd07f79928b046ce07755df3c05fe965a115c305aac3b07",
|
||||
"notes/asd.md": "e791e564d80d7b1354ef6dc05dab000a6ac265518f348d18db855d51264a8671",
|
||||
"notes/Git.md": "33954c7b2255a032d400a87bcec9801fe8251583c092a383bd57f8571e9c7a90",
|
||||
"01. Projects/Electronic Mantainance/List of electronics.md": "cadf1bbd65c88be9d11f8992a4315c24e4a6d14b53b8f197159975217c2e347c",
|
||||
"01. Projects/Fuuka/Outline.md": "6f86cc6dc7ccaa67aca79992e152584ccfe4d73ff0c259e67625c67ded5a47fa",
|
||||
"01. Projects/Juuno/Outline.md": "6bb923894b595091a1d536e77b51b415ef7e69c0d95d59192c134ab23703b42d",
|
||||
"01. Projects/Fuuka/Outline.md": "4a78de980ff0aa4f449ae017423b2a86be6a52f8e9f7594034bdf8fd9974de6d",
|
||||
"01. Projects/Juuno/Outline.md": "c31e2610673db2be6e3064478a37e0dd44cd5cb107bc3c6b70f109467110a51f",
|
||||
"03. Resources/Notetaking/12. Questions.md": "a44916b14a0dd6f11280563bf77918df603b27bc1b43be7d4d41a9e71570cb3e",
|
||||
"03. Resources/Notetaking/CODE Method.md": "76e12aafc17b46efb83b49d0f4f9abe0aaa132770db931766a8cccebfa6a88ec",
|
||||
"03. Resources/Notetaking/North Start.md": "97cc810bb0609df94559cad725c88caec10b4bed33e220451ee6a4a05abb71de",
|
||||
|
|
@ -44,6 +39,17 @@
|
|||
"03. Resources/Notetaking/PARA method.md": "6b97f39b4fb42c6f9cdf52abb0221f9d802e163019fbc53d1a618d02e6ab2aaf",
|
||||
"03. Resources/Notetaking/Progresive Summarization.md": "9236f0faf755cc57e1d6e548f0727f6a2909cc847f8afbef3dc14fa43efa7a44",
|
||||
"01. Projects/Playa/comida.md": "0c27a53d357c1ffab1d4e6bf98545923dc211004041a3233528164ca47b0ef25",
|
||||
"01. Projects/Playa/Comprar.md": "28114ba2502978f1a5a748766ed312dead7472155961ece2cf4b82aee13de322"
|
||||
"01. Projects/Playa/Comprar.md": "28114ba2502978f1a5a748766ed312dead7472155961ece2cf4b82aee13de322",
|
||||
"03. Resources/Notetaking/Habits.md": "2b6688471702b678ce2a8872ef86049592be866948871d8d3fbca5186f663d93",
|
||||
"03. Resources/Development/Git.md": "f019123de154b2b1ea6bb2e60b7f0fe25094c9084c257c04c5e10f1fae0a7126",
|
||||
"03. Resources/Development/Fix messy commits.md": "b1cd788af785ff651eed230eb313b09e984356de8bc2f1ab16b6858e2e5c6acf",
|
||||
"03. Resources/Development/Revert old changes in a codebase.md": "6f2cd7c745d01436eac6ec8360de8085a7f1232f9fd3c21346e1a9ad15800dd2",
|
||||
"03. Resources/Development/Search for a bug.md": "627a609a636453bd9de584ad559ea83338076fe656c08c90604f6551f79cab20",
|
||||
"03. Resources/Development/Feature shipment checklist.md": "e6667e273f6553b4418039564f02dc51d5e83c5455d202cae4daf333914911be",
|
||||
"03. Resources/Development/Iframes.md": "858ffe616925f94e6bfca207282649b111f134e30c67a76ddd0479c4f6193761",
|
||||
"02. Areas/Escape Latam/Canada.md": "974bdb92354ff2d46e67f524390bce477c8b0f753012f4f86eb0d4291b10a6ce",
|
||||
"02. Areas/Escape Latam/Comparación de Paises.md": "6fb41dde50e73d696948be100340f951fc490f40cbcdda45584719fd7ba57194",
|
||||
"02. Areas/Escape Latam/Escapar de Latam.md": "4ac856a52471f5eb6923790a96bbbf9daa867137cf9b8add09f498ba87e4469e",
|
||||
"02. Areas/Escape Latam/New Zeldand.md": "c46e7d6cb08da2a7d5500640abc560f5db37dd056561420bce56df1c6eb485c7"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
---
|
||||
created: 2024-02-10 09:43
|
||||
updated: 2024-02-18 15:29
|
||||
updated: 2024-02-20 11:23
|
||||
---
|
||||
## Maybe
|
||||
|
||||
- Sería interesante tener un chat en el landing junto al "song request"
|
||||
- En el setup entre juno y fuuka en un docker-compose.yml, tendrá menos latencia montar un socket que por TCP?
|
||||
- Implementar un "path mapping" similar a "arr's suit"
|
||||
- Add meilisearch DB on top to add a "search bar" for music
|
||||
- Add the option to allow or deny the song request while a playlist is playing
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
---
|
||||
created: 2024-02-10 09:45
|
||||
updated: 2024-02-18 15:29
|
||||
updated: 2024-02-20 11:48
|
||||
---
|
||||
- Juno sólo necesita que los archivos de música existan cuándo se le solicite ejecutarlo.
|
||||
- Randomize queue, diferente de shuffle mode
|
||||
- randomize, re-order the current queue in a random way
|
||||
- shuffle, pick the next song at random from the current queue
|
||||
- I can use [fd](https://github.com/sharkdp/fd) as reference for the filesystem exploration code
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
created: 2024-02-06 14:27
|
||||
updated: 2024-02-18 15:29
|
||||
updated: 2024-02-20 11:50
|
||||
---
|
||||
- Con el finiquito puedo puedo ir a la AFC y obtener el seguro de cesantía en cuotas, que da más lucas.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
created: 2024-02-06T14:24:00-03:00
|
||||
updated: 2024-02-18 15:29
|
||||
updated: 2024-02-20 11:52
|
||||
type: Checklist
|
||||
---
|
||||
|
||||
|
|
@ -10,3 +10,16 @@ type: Checklist
|
|||
- [ ] Añadir undo-tree to nvim
|
||||
- [ ] Terminar de configurar LF
|
||||
- [ ] Add mpv compact osd
|
||||
- [ ] revisar default merge strategy for git
|
||||
- [ ] Cambiar tipo de ventana para neogit
|
||||
- [ ] Revisar [plugin](https://github.com/vimpostor/vim-tpipeline) to unify nvim and tmux status line
|
||||
- [ ] Revisar ansible para inicializar dots
|
||||
- [ ] Test out Ollama to see if resource usage is too high
|
||||
|
||||
## Obsidian
|
||||
|
||||
- [ ] Revisar plugins
|
||||
- [ ] Media DB plugin
|
||||
- [ ] Obsidian Tasks
|
||||
- [ ] Obsidian Index Folder
|
||||
|
||||
9
03. Resources/Development/Feature shipment checklist.md
Normal file
9
03. Resources/Development/Feature shipment checklist.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
created: 2024-02-20 11:39
|
||||
updated: 2024-02-20 11:42
|
||||
---
|
||||
When a feature or module is shipt, I need to asure the following things before submiting:
|
||||
|
||||
- Clean & readable code
|
||||
- Linter without errors
|
||||
- Design is as close as posible to the proposal
|
||||
25
03. Resources/Development/Fix messy commits.md
Normal file
25
03. Resources/Development/Fix messy commits.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
created: 2024-02-20 11:27
|
||||
updated: 2024-02-20 11:31
|
||||
tags:
|
||||
- dev-tools
|
||||
---
|
||||
# Fix messy commits
|
||||
|
||||
Ya que estas opciones sobre escriben el historial de git, solo deben aplicarse en local y no commits publicados a un remote.
|
||||
|
||||
Como alternativa se puede intentar actualizar el historial remoto siempre y cuando el historial sea igual al local (osea, nosotros fuimos los últimos en actualizarlo y nadie ha hecho nada más). Para esto utilizamos `git push --force-with-lease`.
|
||||
|
||||
## Last commit
|
||||
|
||||
Si solo necesitamos agregar un cambio pequeño al ultimo commit (typo o correr el formatter), podemos aplicarlo con `git commit --ammend`, se puede sobre escribir el mensaje con `-m`.
|
||||
|
||||
## Mutiple commits
|
||||
|
||||
Se pueden arreglar el historial de commits con un `git rebase -i [since commit or branch]` y utilizar las estrategias de pick, squash, reword y drop.
|
||||
|
||||
En caso de que sepamos que haremos un commit que luego no necesitaremos, podemos hacer:
|
||||
- `git commit --fixup [commit hash]` -> descarta el commit message de este commit y mantiene el del commit de referencia
|
||||
- `git commit --squash [commit hash]` -> git juntará los mensajes de todos los commits a hacer squash y el commit de referencia.
|
||||
|
||||
Finalmente podemos hacer `git rebase -i --autosquash` y git eligirá las opciones necesarias a tomar en vez de tener que hacerlo de manera manual.
|
||||
15
03. Resources/Development/Git.md
Normal file
15
03. Resources/Development/Git.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
created: 2024-02-13 22:36
|
||||
updated: 2024-02-20 11:38
|
||||
tags:
|
||||
- dev-tools
|
||||
---
|
||||
## Merge strategies
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
24
03. Resources/Development/Iframes.md
Normal file
24
03. Resources/Development/Iframes.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
created: 2024-02-20 11:43
|
||||
updated: 2024-02-20 11:45
|
||||
---
|
||||
|
||||
## How to make an Iframe 100% it's parent width while maintaining the aspect ratio
|
||||
|
||||
In this example, we need to wrap the iframe in a container class and provide the following styles:
|
||||
|
||||
```css
|
||||
.container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
padding-bottom: 56.25%;
|
||||
}
|
||||
iframe {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
created: 2024-02-20 11:34
|
||||
updated: 2024-02-20 11:34
|
||||
tags:
|
||||
- dev-tools
|
||||
---
|
||||
## Revertir cambios
|
||||
|
||||
Si necesitamos _"deshacer"_ los cambios introducidos en uno o multiples commits, podemos utilizar `git revert --no-edit older_commit_hashˆ..newer_commit_hash`, donde:
|
||||
|
||||
- git realizará un nuevo commit con los cambios contrarios por cada commit en el rango
|
||||
- utilizar `ˆ` en el `old_commit_hash` incluirá ese commit en la reversión de cambios, si no se agrega se empezará a revertir de un commit más adelante.
|
||||
- `--no-edit` es utilizado para que git no nos pregunte por el message de cada nuevo commit
|
||||
- primero debe ser el commit más antiguo, porque git creará nuevos commits en orden provisto y de hacerlo al revés aparecerán conflictos
|
||||
- si solo se quiere revertir un commit, se puede especificar solo ese hash
|
||||
9
03. Resources/Development/Search for a bug.md
Normal file
9
03. Resources/Development/Search for a bug.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
created: 2024-02-20 11:35
|
||||
updated: 2024-02-20 11:37
|
||||
---
|
||||
# Buscar cuando un bug se introdujo
|
||||
|
||||
## Utilizar `git bisect`
|
||||
|
||||
`git bisect` hará un _"binary search"_ entre 2 commits, dejandonos señalar si este commit es _"bueno"_ (no tiene el bug) o _"malo"_ (tiene el bug), permitiendo encontrar el commit que introdujo el bug y facilitar encontrar la causa de este.
|
||||
22
03. Resources/Notetaking/Habits.md
Normal file
22
03. Resources/Notetaking/Habits.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
created: 2024-02-20 11:14
|
||||
updated: 2024-02-20 11:18
|
||||
---
|
||||
# Habits to apply while notetaking process
|
||||
|
||||
## Project checklist
|
||||
|
||||
### Kickoff
|
||||
|
||||
### Completion
|
||||
|
||||
## Periodic reviews
|
||||
|
||||
This should be forgiving, doesn't bad happens if I miss a review day
|
||||
### Weekly
|
||||
|
||||
### Monthly
|
||||
|
||||
## Noticing other habits
|
||||
|
||||
Do this inh small chunks while using the second brain
|
||||
|
|
@ -0,0 +1,412 @@
|
|||
---
|
||||
id: d529f41a-ca28-11ee-97f3-f78c291f6623
|
||||
title: |
|
||||
Debouncing in JavaScript – Explained by Building Auto-Complete Functionality in React
|
||||
status: ARCHIVED
|
||||
tags:
|
||||
- read-later
|
||||
- RSS
|
||||
date_added: 2024-02-12 20:23:30
|
||||
url_omnivore: |
|
||||
https://omnivore.app/me/debouncing-in-java-script-explained-by-building-auto-complete-fu-18da0bc7510
|
||||
url_original: |
|
||||
https://www.freecodecamp.org/news/deboucing-in-react-autocomplete-example/
|
||||
---
|
||||
|
||||
# Debouncing in JavaScript – Explained by Building Auto-Complete Functionality in React
|
||||
|
||||
## Highlights
|
||||
|
||||
Debouncing accepts a function and transforms it in to an updated (debounced) function so that the code inside the original function is executed after a certain period of time.
|
||||
|
||||
[source](https://omnivore.app/me/debouncing-in-java-script-explained-by-building-auto-complete-fu-18da0bc7510#2c8f31bd-f011-49bd-99bc-36192f7fd823)
|
||||
|
||||
---
|
||||
|
||||
function debounce(func, delay) {let timeout=null return (...args) => {if(timeout) clearTimeout(timeout) timeout=setTimeout(() \=> { func(...args) timeout=null }, delay) } }
|
||||
|
||||
[source](https://omnivore.app/me/debouncing-in-java-script-explained-by-building-auto-complete-fu-18da0bc7510#5a57c802-520a-409a-a51c-e554a6ec8bd5)
|
||||
|
||||
---
|
||||
|
||||
const useDebounce = (func, delay) => { let timeout\=null return (...args) => {if(timeout) clearTimeout(timeout)timeout\=setTimeout(() => { func(...args) }, delay) } }export default useDebounce
|
||||
|
||||
[source](https://omnivore.app/me/debouncing-in-java-script-explained-by-building-auto-complete-fu-18da0bc7510#226d8c7b-6900-4b2f-a705-5f5b6e10afc5)
|
||||
|
||||
---
|
||||
|
||||
## Original
|
||||
|
||||

|
||||
|
||||
Hi readers, I hope you are doing great! I am back with another tutorial on web development. If you are someone who enjoys developing web apps with JavaScript and React, then this post is for you.
|
||||
|
||||
When you roll out a new app into production, you want to make sure that it's user friendly. A website's performance is a key part of the user experience. Every user wants the website and its contents to load quickly. Each and every second is valuable and could result into a user never visiting your website again.
|
||||
|
||||
In this guide, we are going to understand a very important technique in JavaScript known as debouncing. Then, I will show you how to implement the autocomplete functionality in React with debouncing.
|
||||
|
||||
Now, in order to get the most out of this tutorial, I am assuming you have a basic knowledge of JavaScript. If you need to get started or review, here are a couple resources for you:
|
||||
|
||||
* Learn JavaScript basics – [handbook for beginners](https://www.freecodecamp.org/news/learn-javascript-for-beginners/)
|
||||
* The freeCodeCamp [JavaScript Algorithms and Data Structures certification](https://www.freecodecamp.org/news/learn-javascript-with-new-data-structures-and-algorithms-certification-projects/)
|
||||
|
||||
## **Table of Contents:**
|
||||
|
||||
* [What is Debouncing?](#what-is-debouncing)
|
||||
* [How to Implement Debouncing in JavaScript](#how-to-implement-debouncing-in-javascript)
|
||||
* [Use Case of Debouncing](#use-case-of-debouncing)
|
||||
* [Conclusion](#conclusion)
|
||||
|
||||
## What is Debouncing?
|
||||
|
||||
Debouncing is a strategy used to improve the performance of a feature by controlling the time at which a function should be executed.
|
||||
|
||||
==Debouncing accepts a function and transforms it in to an updated (debounced) function so that the code inside the original function is executed after a certain period of time.==
|
||||
|
||||
If the debounced function is called again within that period, the previous timer is reset and a new timer is started for this function call. The process repeats for each function call.
|
||||
|
||||
An example will help you understand better. Let's take a function `fun()`. We want this function to execute after 500ms.
|
||||
|
||||
```crystal
|
||||
function fun() {
|
||||
console.log('This is a function')
|
||||
}
|
||||
```
|
||||
|
||||
After debouncing, a new function `debouncedFun()` is returned. Now, whenever you call `debouncedFun()`, it will be called after 500ms.
|
||||
|
||||
If you call it again within the next 500ms after first calling it, the previous timer is reset and a new timer is started for the second function call. The process repeats if you keep calling the function within 500ms.
|
||||
|
||||
## How to Implement Debouncing in JavaScript
|
||||
|
||||
Let's understand how to implement debouncing in JavaScript. First, we'll go over our requirements. What behavior do we want from the debounced function?
|
||||
|
||||
* Delay the function execution by a certain time, `delay`.
|
||||
* Reset the timer if the function is called again.
|
||||
|
||||
To debounce a function, we'll have a separate function that accepts the function reference and the delay as parameters, and returns a debounced function.
|
||||
|
||||
```ada
|
||||
function debounce(func, delay) {
|
||||
return () => {} // return debounced function
|
||||
}
|
||||
```
|
||||
|
||||
This function will only be called once to return a debounced function and that, in turn, will be used in the subsequent code.
|
||||
|
||||
To delay a function by some milliseconds, we can simply use the `setTimeout` function in JavaScript.
|
||||
|
||||
```arcade
|
||||
function debounce(func, delay) {
|
||||
return () => {
|
||||
setTimeout(() => {
|
||||
func()
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This delays the function call by `delay` milliseconds. But this is incomplete as it only satisfies the first requirement. How do we achieve the second behaviour?
|
||||
|
||||
Let's create a variable `timeout` and assign it to the return value of `setTimeout` method. The `setTimeout` method returns a unique identifier to the timeout, which is held by `timeout` variable.
|
||||
|
||||
```javascript
|
||||
function debounce(func, delay) {
|
||||
let timeout=null
|
||||
return () => {
|
||||
timeout=setTimeout(() => {
|
||||
func()
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Each time you invoke `setTimeout`, the ID is different. We will use this `timeout` variable to reset the timer.
|
||||
|
||||
But how do we get access to `timeout` from outside the `debounce()` method? As mentioned before, `debounce()` is only used once to return a debounced function. This, in turn, performs the debouncing logic.
|
||||
|
||||
Then, how does the debounced function have access to `timeout` even if it is used outside the `debounce()` function? Well, it uses a concept called closure.
|
||||
|
||||
### What's a closure in JavaScript?
|
||||
|
||||
In JavaScript, an inner function always has access to the local variables of the outer function. In our case, the inner function has access to `timeout` that has function level scope in the `debounce()` method.
|
||||
|
||||
But when the outer function returns this inner function, the inner function still holds a reference to the local variables of the outer function long after the outer function has finished execution. This is the concept of a closure.
|
||||
|
||||
Let's understand closures with an example.
|
||||
|
||||
```javascript
|
||||
function outerFunction() {
|
||||
const x = 5;
|
||||
|
||||
return () => {
|
||||
console.log(x);
|
||||
}
|
||||
}
|
||||
|
||||
const inner = outerFunction();
|
||||
|
||||
inner(); // prints 5
|
||||
|
||||
// console.log(x) Throws reference error
|
||||
```
|
||||
|
||||
Here, if we call `inner()`, the code runs without any errors and prints 5\. But, if we try to access `x` directly, JavaScript throws a reference error.
|
||||
|
||||

|
||||
|
||||
JavaScript Reference Error
|
||||
|
||||
Here, `inner()` closes over `x` and only this function can use the variable and no one other one can. We cannot access the variable explicitly.
|
||||
|
||||
You can check out [this beginner-friendly tutorial](https://www.freecodecamp.org/news/closures-in-javascript/) to learn more about closures.
|
||||
|
||||
### Back to Debouncing
|
||||
|
||||
Let's get back to where we left off:
|
||||
|
||||
```javascript
|
||||
function debounce(func, delay) {
|
||||
let timeout=null
|
||||
return () => {
|
||||
timeout=setTimeout(() => {
|
||||
func()
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here, JavaScript uses a closure to hold access to `timeout` every time we use the debounced function.
|
||||
|
||||
Let's use this to our advantage. Since `debouncedFun()` has access to the same `timeout` variable in every function call, we can add a condition to check whether a previous timeout exists. We can simply do this with a null check, `if(timeout !== null)` or `if(timeout)`.
|
||||
|
||||
Then, we use the `clearTimeout()` method to cancel the previous timeout, thus resetting the timer.
|
||||
|
||||
Add the following statement before starting a new timeout:
|
||||
|
||||
```lisp
|
||||
if(timeout)
|
||||
clearTimeout(timeout)
|
||||
|
||||
```
|
||||
|
||||
Once the timeout is reset, a new timeout is started for the current function call, whose ID is then assigned to `timeout`. The process is repeated for the subsequent function calls who have access to the same `timeout` due to closures.
|
||||
|
||||
```javascript
|
||||
function debounce(func, delay) {
|
||||
let timeout=null
|
||||
return () => {
|
||||
if(timeout) clearTimeout(timeout)
|
||||
|
||||
timeout=setTimeout(() => {
|
||||
func()
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
With this, we have satisfied our second requirement – that is, resetting the timer and starting a new one. It's time to use this debounced function.
|
||||
|
||||
Let's pass `fun()` to the `debounce()` method with a delay of 500ms.
|
||||
|
||||
```kotlin
|
||||
const debouncedFun = debounce(fun, 500)
|
||||
|
||||
```
|
||||
|
||||
`debouncedFun()` is basically `fun()` with debouncing behaviour. Let's call this function at different time intervals to test our functionality.
|
||||
|
||||
```stylus
|
||||
debouncedFun()
|
||||
|
||||
setTimeout(debouncedFun, 300)
|
||||
|
||||
setTimeout(debouncedFun, 900)
|
||||
```
|
||||
|
||||
The first function call is made instantly. The other two are made after 300ms and 900ms respectively. Can you guess the output?
|
||||
|
||||
The code prints `This is a function` two times. Let's understand why. Here, after the first call is made, `fun()` is scheduled to execute after 500ms. But the second one is made in 300ms which resets the timer and starts a new one.
|
||||
|
||||
500ms have passed and the `fun()` method executes. Then, at 900ms, another function call is made. This again executes `fun()` after 500ms.
|
||||
|
||||
There is still a small improvement we should make. Our logic does not consider function arguments. Let's replace `fun()` with `fun(a, b)`.
|
||||
|
||||
```javascript
|
||||
function fun(a, b) {
|
||||
console.log(`This is a function with arguments ${a} and ${b}`)
|
||||
}
|
||||
```
|
||||
|
||||
To incorporate arguments while debouncing, return a debounced function that accepts arguments.
|
||||
|
||||
```javascript
|
||||
function debounce(func, delay) {
|
||||
let timeout=null
|
||||
return (...args) => {
|
||||
if(timeout) clearTimeout(timeout)
|
||||
|
||||
timeout=setTimeout(() => {
|
||||
func(...args)
|
||||
timeout=null
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By using the spread operator, any arguments passed to the debounced function will be stored as an array in the `args` variable. Then, spread out the same `args` array to call the actual function with the arguments passed.
|
||||
|
||||
```kotlin
|
||||
const debouncedFun=debounce(fun, 500)
|
||||
debouncedFun(2,3)
|
||||
```
|
||||
|
||||
The above code prints `This is a function with arguments 2 and 3` after 500ms.
|
||||
|
||||
## Use Case of Debouncing
|
||||
|
||||
Let's see how debouncing is used in practical applications. The most common use case of debouncing is the autocomplete functionality. You must have seen many websites where you type into an input field and it shows a list of results as you type them.
|
||||
|
||||
Here's an example from Google Search:
|
||||
|
||||

|
||||
|
||||
Google Search Autocomplete after typing in "Top 10"
|
||||
|
||||
Google search shows the most recent and commonly searched terms. The information is mostly fetched from the browser cache. But, several websites make API calls to backend server to fetch the data from a database.
|
||||
|
||||
This can easily be implemented by adding an `onchange` event to the `input` element and implementing the fetch logic in the event handler. But there's a slight issue with this.
|
||||
|
||||
Consider the following example:
|
||||
|
||||

|
||||
|
||||
API Request made for each input value
|
||||
|
||||
When I type the word _absolute_, an API request is made every time the value of the input field changes. We are making 8 API requests in very few milliseconds which puts a lot of load on the backend server and could cause performance issues.
|
||||
|
||||
Ideally, we want to show the auto-complete results some time after the user has finished typing. Here, the user has typed _absolute_ in one go, so instead of showing results every time the input changes, we could show them once the user has finished typing – that is, we could add some delay between the input change and the results being displayed.
|
||||
|
||||
So, we only make the API calls when the user finishes typing their word and not on every input change. This reduces the number of API calls and improves performance. We can achieve this behavior with debouncing.
|
||||
|
||||
Let's understand how to implement the autocomplete functionality in React.
|
||||
|
||||
### Auto-complete example
|
||||
|
||||
Use `create-react-app` (or a modern build tool like Vite) to create the project. Remove the existing boilerplate code. There is no need to install any additional dependencies. Run `npm start` command to start the project. You can find the complete code on [GitHub](https://github.com/KunalN25/react-debouncing).
|
||||
|
||||
I have set up a Node server to fetch data for the app. You can find it in the Git repo. Run the `node server` command to start it. I am not going to show the Node.js code as it's out of the scope of this tutorial.
|
||||
|
||||
Let's get started with the implementation. We will write a simple autocomplete functionality. The app should show a list of cities that contain an input string typed by the user.
|
||||
|
||||
#### App Component
|
||||
|
||||
We'll first need an `input` element to accept user input and a _results container_ for the search results. Attach an event handler to the `input` element which is an `async` function since it will include the fetching logic.
|
||||
|
||||
```javascript
|
||||
function App() {
|
||||
const [data, setData] = useState(null)
|
||||
|
||||
const loadData = async (event) => {
|
||||
|
||||
}
|
||||
return (
|
||||
<div className="App">
|
||||
<input type="text" onChange={(e) => loadData(e)}/>
|
||||
{data && data.length !== 0 &&
|
||||
<div className="results-container">
|
||||
{data.map(item => (
|
||||
<div key={item.id} className="result-item">
|
||||
<p> {item.city} </p>
|
||||
</div>
|
||||
))}
|
||||
</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
The data will be stored as state and the results will only be shown if the data is non-empty. I'll skip over the CSS for this tutorial, you can find it in the [Git Repo](https://github.com/KunalN25/react-debouncing).
|
||||
|
||||
#### Event Handler
|
||||
|
||||
The `loadData()` function fetches our data and stores the response as state.
|
||||
|
||||
```cs
|
||||
const loadData = async (event) => {
|
||||
const value=event.target.value
|
||||
if(value === '') {
|
||||
setData(null)
|
||||
return
|
||||
}
|
||||
const response=await fetch(`http://localhost:8000/data/${value}`)
|
||||
const res=await response.json()
|
||||
setData(res)
|
||||
}
|
||||
```
|
||||
|
||||
If no value is entered, simply exit the function. Else, make the request to the node server endpoint. This function is called every time the input changes, so we will debounce this function.
|
||||
|
||||
#### Debounce Implementation using a Custom Hook
|
||||
|
||||
We will write the debouncing logic inside a custom hook. The advantage of custom hooks is that you can re-use the same logic throughout your application. It is highly advisable to do so.
|
||||
|
||||
Create a new folder `custom-hooks` and inside it, create a file `useDebounce.js`. As explained before, the `useDebounce()` method should take a function and delay as parameters and return the debounced function.
|
||||
|
||||
```routeros
|
||||
const useDebounce = (func, delay) => {
|
||||
let timeout=null
|
||||
|
||||
return (...args) => {
|
||||
if(timeout) clearTimeout(timeout)
|
||||
|
||||
timeout=setTimeout(() => {
|
||||
func(...args)
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
|
||||
export default useDebounce
|
||||
```
|
||||
|
||||
Now, inside the app component, call this method once to get `loadDataDebounced()`.
|
||||
|
||||
```angelscript
|
||||
const loadDataDebounced = useDebounce(loadData, 400)
|
||||
|
||||
```
|
||||
|
||||
We'll use this new method as the event handler for the `input` element.
|
||||
|
||||
```reasonml
|
||||
<input type="text" onChange={(e) => loadDataDebounced(e)}/>
|
||||
|
||||
```
|
||||
|
||||
#### Output
|
||||
|
||||
Enter a search string inside the `input` element to test our code.
|
||||
|
||||

|
||||
|
||||
On-screen output
|
||||
|
||||

|
||||
|
||||
As you can see in the Network tab, only one request is getting sent instead of three. This makes the search performance much better.
|
||||
|
||||
## Conclusion
|
||||
|
||||
In this tutorial, you learned what debouncing is and how it is implemented. Debouncing delays the function execution by a certain time and resets the previous timer if the function is called again.
|
||||
|
||||
Debouncing uses the important concept of closures. I took a slight detour from the implementation to explain what closure is. It can be a confusing concept for beginners, so take your time understanding it. Closures allow you to work with local variables even after a function has finished execution.
|
||||
|
||||
After that, I showed you a popular use case of debouncing, the auto-complete functionality. The performance of the feature can be improved with debouncing. I also showed you how to implement auto-complete in React and use debouncing with custom hooks. I hope this helps you in future projects.
|
||||
|
||||
If you are unable to understand the content or find the explanation unsatisfactory, let me know. New ideas are always appreciated! Feel free to connect with me on Twitter. Till then, Goodbye!
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers. [Get started](https://www.freecodecamp.org/learn/)
|
||||
41
notes/Git.md
41
notes/Git.md
|
|
@ -1,41 +0,0 @@
|
|||
---
|
||||
created: 2024-02-13 22:36
|
||||
updated: 2024-02-18 15:29
|
||||
tags:
|
||||
- dev-tools
|
||||
---
|
||||
## Merge strategies
|
||||
|
||||

|
||||
|
||||
## Buscar cuando un bug se introdujo
|
||||
|
||||
Utilizar `git bisect`
|
||||
|
||||
## Fix messy commits
|
||||
|
||||
Ya que estas opciones sobre escriben el historial de git, solo deben aplicarse en local y no commits publicados a un remote.
|
||||
|
||||
### Last commit
|
||||
|
||||
Si solo necesitamos agregar un cambio pequeño al ultimo commit (typo o correr el formatter), podemos aplicarlo con `git commit --ammend`, se puede sobre escribir el mensaje con `-m`.
|
||||
|
||||
### Mutiple commits
|
||||
|
||||
Se pueden arreglar el historial de commits con un `git rebase -i [since commit or branch]` y utilizar las estratégias de pick, squash, reword y drop.
|
||||
|
||||
En caso de que sepamos que haremos un commit que luego no necesitaremos, podemos hacer:
|
||||
- `git commit --fixup [commit hash]` -> descarta el commit message de este commit y mantiene el del commit de referencia
|
||||
- `git commit --squash [commit hash]` -> git juntará los mensajes de todos los commits a hacer squash y el commit de referencia.
|
||||
|
||||
Finalmente podemos hacer `git rebase -i --autosquash` y git eligirá las opciones necesarias a tomar en vez de tener que hacerlo de manera manual.
|
||||
|
||||
## Revertir cambios
|
||||
|
||||
Si necesitamos _"desacer"_ los cambios introducidos en uno o multiples commits, podemos utilizar ˋgit revert --no-edit older_commit_hashˆ..newer_commit_hashˋ, donde:
|
||||
|
||||
- git realizará un nuevo commit con los cambios contrarios por cada commit en el rango
|
||||
- utilizar ˋˆˋ en el ˋold_commit_hashˋ incluirá ese commit en la reversión de cambios, si no se agrega se empezará a revertir de un commit más adelante.
|
||||
- ˋ--no-editˋ es utilizado para que git no nos pregunte por el message de cada nuevo commit
|
||||
- primero debe ser el commit más antiguo, porque git creará nuevos commits en orden provisto y de hacerlo al revez aparecerán conflictos
|
||||
- si solo se quiere revertir un commit, se puede especificar solo ese hash y ya
|
||||
Loading…
Add table
Add a link
Reference in a new issue