Cory House Profile picture
Oct 31, 2022 5 tweets 1 min read Read on X
⚛️10 reasons to prefer useReducer over useState:

1.  Separates reads from writes.

2. Avoids useState pitfalls. You should use functional setState to safely update state using existing state. With useReducer, you can't mess this up.

3. Easy to test. It's a pure function.

#react
4. Typically simplifies useEffect by removing unnecessary dependencies

5. Centralizes state writes, which is often easier to understand

6. Elegantly handles related state (Note you can also use objects with useState to compose data that changes together)
7. Can easily store the entire state tree in localStorage

8. Can pass dispatch down to avoid passing many callback funcs on props

9. Can easily log all actions since they're centralized

10. Moving state management to a separate file makes the component easier to understand.
useReducer decouples update logic from describing what happened. This helps remove unnecessary dependencies from effects and avoids re-running them more often than necessary.
If you find yourself passing multiple callbacks down via props, consider refactoring to useReducer.

Then you can just pass dispatch down instead. 👍

• • •

Missing some Tweet in this thread? You can try to force a refresh
 

Keep Current with Cory House

Cory House Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @housecor

Sep 25, 2023
Problem: You want to assure your web app isn’t logging messages to the console, and enforce it on CI.

Solution: Use Playwright.

With these 3 lines, I can fail a test if anything is written to the console. I can also be more specific and only fail if a console error occurs. // To keep the console clean, throw an error to fail  // the test immediately if a console message occurs. page.on("console", (message) => {   // Optionally, can check for only errors like this:    // if (message.type() === "error") {   throw new Error(message.text()); });
To make this easy to apply to every test, I created my own page goto function that also applies this console config. import { Page } from "@playwright/test";  // To keep the console clean, throw an error to fail the test immediately if a console message occurs. export function throwOnConsole(page: Page) {   page.on("console", (message) => {     // Optionally, can check for only errors like this: if (message.type() === "error") {     throw new Error(message.text());   }); }  // Load a page and configure the console to throw an error if a console message occurs. export async function goTo(page: Page, path: string) {   await page.goto(path);   throwOnConsole(page); }
Many people mentioned ESLint. It’s great, but it can’t catch runtime logs from npm packages.
Read 4 tweets
Jun 15, 2023
Problem:
When you deploy a new version of your SPA, users with a tab open will keep using the old SPA code.

Solution:
1. Specify the app version in each HTTP call from the UI via a x-app-version header.

2. Validate the x-app-version header on the API server. If x-app-version… twitter.com/i/web/status/1…
If you prefer, you can use HTTP 422 instead. It's more specific than a 400, and feels like a good fit for this use case.

And the header name can be whatever you like. Anything that starts with "x-" is a fine choice.

developer.mozilla.org/en-US/docs/Web…
Many excellent suggestions in this thread.

Other good options:

1. You don't have to return an HTTP 400 and force the user to update their browser. The API can reply with a successful request instead (like an HTTP 205), or include an "x-stale-ui" header in the response to notify… twitter.com/i/web/status/1…
Read 5 tweets
Jun 9, 2023
A bug I see in nearly every web app: If someone saves a stale record, it overwrites the newer DB record. 😬

Solution:
1. Provide 'updated' time with each GET

2. Require 'updated' when sending a POST/PUT

3. If 'updated' doesn't match the DB, return an HTTP 409 (conflict) Image
When the API returns an error status because the record was stale, tell the user "The record you tried to save has been changed by someone else."

Then, 4 options:
1. Tell the user to reload and try again (Easiest and safest, but annoying for the user)

2. Provide a "Save anyway"… twitter.com/i/web/status/1…
If you prefer, etags can solve this problem, and also have the benefit of potentially saving some bandwidth via caching.

But, I find the etag spec clunkier to implement and work with.

More on etags: developer.mozilla.org/en-US/docs/Web…
Read 5 tweets
Apr 26, 2023
Hard truth: Some devs are WAY more productive.

As a consultant, I work with dozens of teams. I see massive differences in developer output.

Here's an example on the same team:
Dev 1: 45 PRs/month
Dev 2: 1 PR/month

I did code reviews. Dev 1 had higher code quality too.
And yes, stats can be gamed. So, I don't recommend managing based on stats. But when stats are wildly lopsided, there's often a root cause worth exploring.

In this case, the dev who completed 45 PRs had many PRs that were more complex than the 1 PR merged by the other dev.
A related law:

Price's law: 50% of the work is done by the square root of the number of employees.

So:
If you have 10 employees, 3 do 50% of the work.

If you have 100 employees, 10 do 50% of the work.

If you have 10k employees, 100 do 50% the work.
Read 4 tweets
Apr 1, 2023
Continuous integration:
✅ No PRs
✅ No branches
✅ No gating code reviews
✅ Commit directly into trunk

Frequent Integration:
✅ Small PRs
✅ Simple, short-lived feature branches
✅ Code review before merge
✅ Merge branches ~daily

Frequent integration is often good enough.
For many teams, "frequent integration" is the sweet spot.

Doing code reviews before merge assures reviews actually happen. (you can pair, but few devs do consistently, and outside reviews are useful when pairing too)

Integrating work approximately daily is frequent enough.
In summary, truly Continuous Integration is rarely practical. That's why it's rarely practiced.

It requires a very mature team:
Fast CI
High trust
Frequent pairing
Robust feature flags
Superb test coverage
Consistently high code quality

That's a high bar to clear.
Read 4 tweets
Mar 23, 2023
TypeScript tip: Try to avoid optional fields.

They cause two problems:
1. They reduce type safety.
2. There's no type-safe way to enforce when the field should be populated.

Solution: Often optional fields can be eliminated by declaring separate and more specific types. // Optional field 👎 interface Car {   model: string;   as
Principle: The more information we can provide TypeScript, the more it can help us.
The problem is, an optional field doesn't specify when it must be populated.

This creates two risks:

1. An optional field may not be populated when it logically should be.

2. An optional field may be populated when it logically shouldn't be.
Read 5 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Don't want to be a Premium member but still want to support us?

Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal

Or Donate anonymously using crypto!

Ethereum

0xfe58350B80634f60Fa6Dc149a72b4DFbc17D341E copy

Bitcoin

3ATGMxNzCUFzxpMCHL5sWSt4DVtS8UqXpi copy

Thank you for your support!

Follow Us!

:(