Cory House Profile picture
Sep 14, 2021 12 tweets 5 min read Read on X
10 lessons I've learned about handling React state over the last 7 years...

(thread)

#react #reactjs
#1: Know the 8 ways to handle state, and when to consider each. Image
#2: Class components are dead to me. Image
#3: “Normalize” state by deriving on render. ImageImage
#4: Understand when React renders, and how to avoid it. Image
#5: Most state is remote state. Streamline remote state via a custom "useApi" hook, react-query, or swr.

The result? WAY less code.

react-query/swr add:
✅Caching
✅Invalidates & refetches stale data
✅Dedupes requests
✅Auto retries
✅Refetch on refocus/reconnect
#6: Start local, then lift. Global is a last resort. Prop drilling is no biggie. Image
#7: Wrap each context in a single file.

✅Streamlines call sites
✅Provides useful error if context is consumed without the provider in a parent. Image
#8: You don’t need LoDash, Underscore, Ramda. Embrace immutable JS features. Image
#9: You don’t need a form library. You need a pattern. Image
#10: State Enums are 🔥. With simple state enums, you likely don’t need XState and state charts (though they are nice). ImageImage
Was this useful? This is a little taste of all the React state tips I cover in "Managing React State" on @pluralsight.

Free trial: pluralsight.com/pricing/free-t…

Full course: app.pluralsight.com/library/course…

• • •

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!

:(