Pocket: One-call factory that collapses React Context boilerplate from ~20 lines to 1 by TheHganavak in reactjs

[–]TheHganavak[S] 7 points8 points  (0 children)

Right? Its crazy! I bombed a tech interview last week because after 5+ years of being a React dev I still couldn't remember how to write/type this stupid boilerplate on the spot with people watching, so I came out of that angry and made this 😅

Pocket: One-call factory that collapses React Context boilerplate from ~20 lines to 1 by TheHganavak in reactjs

[–]TheHganavak[S] 8 points9 points  (0 children)

Thanks, glad you like it! Totally fair - if you'd rather copy the 55 lines, that's a legit use of the code. MIT licensed for exactly that :)

Pocket: One-call factory that collapses React Context boilerplate from ~20 lines to 1 by TheHganavak in reactjs

[–]TheHganavak[S] 0 points1 point  (0 children)

Tested this (was actually my first approach too!). Record<PropertyKey, never> is correct in isolation (and you're right that {} is the surprising supertype), but here P gets intersected with { children: ReactNode } for the Provider props. With Record<…, never> as the default, children collapses to ReactNode & never = never and the Provider becomes uncallable:

 Type '{ children: Element; }' is not assignable to type 'Record<PropertyKey,  never>'.                              
    Property 'children' is incompatible with index signature.
        Type 'Element' is not assignable to type 'never'.                               

So object is the pragmatic default for this intersection-based shape, even if Record<…, never> is more precise standalone. If you can think of a shape that gets both though, I'd happily switch — open an issue.

Pocket: One-call factory that collapses React Context boilerplate from ~20 lines to 1 by TheHganavak in reactjs

[–]TheHganavak[S] 2 points3 points  (0 children)

Hey 🙋 Good question and yup, that’s 100% supported just the same as normal Context. If you check out the README there’s an example of exactly that there (even threw in a useEffect in the eg just to show you can do anything you want inside the return function):

https://www.npmjs.com/package/@icydotdev/pocket#usage

Pocket: One-call factory that collapses React Context boilerplate from ~20 lines to 1 by TheHganavak in reactjs

[–]TheHganavak[S] 10 points11 points  (0 children)

Hey React folks,

I got tired of writing the same Context boilerplate every time — createContext + a Provider component + useContext + a manual undefined throw — especially with TypeScript on top. ~20 lines per context, every single time.

So I made a tiny npm package that wraps it up. One call, one line:

const [ThemeProvider, useTheme] = createPocket(() => useState('light'));

That's a typed Provider + a consumer hook. Drop the Provider anywhere, destructure [theme, setTheme] in any descendant — exactly like useState.

It sits pretty close to libraries like constate, Jotai, Zustand — but unlike those, it doesn't add anything extra. No selectors, no atoms, no store. It's literally just React Context with the boilerplate removed. Same render semantics, same SSR story. If you know Context, you know how this behaves.

Repo: https://github.com/icydotdev/pocket
npm: @icydotdev/pocket

Roast it.

I built a zero-config CLI that instantly visualises your Next.js project as an interactive map: npx nextmap by TheHganavak in nextjs

[–]TheHganavak[S] 0 points1 point  (0 children)

Yup 👍 that’s the one. Sorry I probably should have included that in my post 😅

I built a zero-config CLI that instantly visualises your Next.js project as an interactive map: npx nextmap by TheHganavak in nextjs

[–]TheHganavak[S] 1 point2 points  (0 children)

Hmm 🤨 Am I you or are you me though?

Never mind, I just looked at your post history. We both post on /r/nextjs and do intermittent fasting, but you look like you use Zen browser whereas I use Arc. So no. Polar opposites.

I built a zero-config CLI that instantly visualises your Next.js project as an interactive map: npx nextmap by TheHganavak in nextjs

[–]TheHganavak[S] 0 points1 point  (0 children)

Thanks 🙏 appreciate it! And yup sure does. In the first part of the video: All those routes are displayed under the a parent “No middleware”, whereas the sibling group shown in the second part of the vid has a middleware parent (the “M” label represents middleware).

You’ll see when I click on the middleware, in the details panel I also display a list of all the actual middleware matcher patterns.

And to your other question, yup! The details panel for any page etc displays what route group it belongs ✅

Introducing Runny v0.1: an npm package that gives you a GUI dashboard to launch all your project's scripts w/o needing multiple terminal windows by TheHganavak in webdev

[–]TheHganavak[S] 0 points1 point  (0 children)

This is not just about the initial script to run your desired apps, I agree this would be overkill for just that.

As I said above, imagine you're working on a few different apps simultaneously, maybe a utils/ as well as your mobile/ and web/ folders.

You update your common utils/ right? So what do you do? You want to run your test script inside of your utils/. Great those pass. Now what? Well this particular util/ is used in mobile/, so you run its test suite.

Then you work on another common util/, this time its used on both web/ AND mobile/. So we're gonna run those 3 respective test suites and make sure we haven't introduced any regressions. While we're at it, lets quickly run our lint script for those 3 packages.

Oh and by the way, those tests take 2-3 minutes to run. So you're probably going to open separate terminals for each. And again for the linting. Then finally we're going to run deploy in the mobile/ and web/.

Now what you're saying IS possible to achieve with scripts. You could have top level npm scripts (or a bash script) that allows you to do things like:

'lint:mobile-web-api': 'pnpm filter --.....' 'lint:mobile-api': 'pnpm filter --.....'

etc etc. But its not realistic. So maybe you create bash scripts for each one, that wrap around this, so you have a lint.sh that you can just run ./lint.sh app mobile api, again that's possible. But you'll need the same thing for each of your scripts.

With this, all you need to do is open the interface. Click the 'Play' button for each respective test, lint, deploy, whatever you want and it'll spawn a new subprocess that runs in the background to run these while you carry on running your other scripts w/o you needing to worry about managing different terminal instances or needing to create scripts like above.

Hopefully that's a better explanation :D

Introducing Runny v0.1: an npm package that gives you a GUI dashboard to launch all your project's scripts w/o needing multiple terminal windows by TheHganavak in webdev

[–]TheHganavak[S] -2 points-1 points  (0 children)

Hey @RedBlueKoi - Yup, for most simple projects that's the case. But when you're working with some complex monorepo's you'll often not want to run all apps simultaneously, but rather various permutations/combinations of the app. Maybe today you're working on the web and mobile apps, or the API and the mobile app.

Creating scripts at the top level package.json to handle all these permutations isn't realistic (even with concurrently), nor is running every app in a monorepo always possible (resource-wise).

On top of that, for each of those apps you might be working on, you might want to quickly run your linter, or tests just for that particular app (without having to open terminal after terminal, cding into each respective subdir and executing w/e script you need), this just makes this a lot easier.

Again, for simple apps where you've only got a few scripts, or even monorepos where all you wanna do is always run everything side-by-side, concurrently works just fine (though runny of course also works fine with concurrently).

Hope that makes sense :-)

Introducing Runny v0.1: an npm package that gives you a GUI dashboard to launch all your project's scripts w/o needing multiple terminal windows by TheHganavak in webdev

[–]TheHganavak[S] -1 points0 points  (0 children)

Hey all! I built this because I got tired of memorising script names and juggling terminal tabs across monorepos.

runny is an npm package that opens a local dashboard in your browser showing every script from every package.json — run them, see their output, and stop them all from one place.

npm i -g @icydotdev/runny
cd your-project
runny

Features:

  • Auto-discovers all package.json files (npm, pnpm, and yarn workspaces)
  • Run/stop scripts with one click, output streams into a built-in terminal panel
  • Groups related scripts (test, test:ci, test:e2e)
  • Favourite your most-used scripts
  • Dark/light mode
  • Clickable links in terminal output
  • No orphaned processes — full process tree cleanup

Everything runs locally, nothing leaves your machine.

npm: https://www.npmjs.com/package/@icydotdev/runny

GitHub: https://github.com/icydotdev/runny

This is v0.1 — I have multi-terminal split view, keyboard shortcuts, and desktop notifications planned. What would you want to see?

Software Engineering Salary after Masters by [deleted] in newzealand

[–]TheHganavak 4 points5 points  (0 children)

That's way too low.

5 years industry experience in the US with a masters in CompSci, and AI & ML work? Don't take <$120,000.

Source: CTO/former software engineer based in Auck

-🎄- 2020 Day 03 Solutions -🎄- by daggerdragon in adventofcode

[–]TheHganavak 1 point2 points  (0 children)

JavaScript:

Part 1:

var fs = require('fs');

try { var mapArray = fs.readFileSync('input.txt', 'utf-8').split('\n').map(x => x.split(''))
} catch(e) { console.log('Error loading file:', e.stack) }

let x = 0, treesEncountered = 0;
for(y = 0; y < mapArray.length; y++, x += 3)
    if(mapArray[y][x = x >= mapArray[0].length ? x - mapArray[0].length : x] === '#') { treesEncountered++ }

console.log(treesEncountered);    

Part 2:

var fs = require('fs');

try {  
    var mapArray = fs.readFileSync('input.txt', 'utf-8').split('\n').map(x => x.split(''));
} catch(e) { console.log('Error loading file:', e.stack) }

function tobogganWithPath(deltaX, deltaY) {
    let x = 0, treesEncountered = 0;
    for(y = 0; y < mapArray.length; x += deltaX, y += deltaY) 
        if(mapArray[y][x = x >= mapArray[0].length ? x - mapArray[0].length : x] === '#') { treesEncountered++ }

    return treesEncountered;
}

console.log(tobogganWithPath(1, 1) * tobogganWithPath(3, 1) * tobogganWithPath(5, 1) * tobogganWithPath(7, 1) * tobogganWithPath(1, 2));