And yes, many of these use useEffect behind the scenes. But that’s the point - instead of calling useEffect directly, you should probably use a mature abstraction at this point.
Just learned a new monorepo pattern from @rwieruch: Incubate and hatch.
Goal: Compose a separate repo within a monorepo. This is useful when the repo will be developed initially in a monorepo (incubated), and handed to a separate team (hatched).
1/4👇
Here's the incubate and hatch approach:
1. Create a separate repo.
2. Close the new repo into your existing monorepo. (Called incubation). Ignore the repo via .gitignore. This allows rapid dev by referencing local versions of relevant monorepo dependencies.
2/4
3. When the project is ready to be handed to the separate team, the repository is "hatched". Since the project had a dedicated repo all along, this is easy. Relevant dependencies are set to the current published version. The new team can upgrade deps over time, as desired.
3/4
1. Unit tests (testing functions and components in isolation, often via @fbjest)
2. In-browser tests (testing the app in the browser, often via @Cypress_io)
The struggle: How do we avoid testing the same things twice?
Two approaches I've seen:
1. Focus mostly on unit testing, and create a small number of in-browser "happy path" tests.
2. Focus mostly on in-browser testing, and only create unit tests when desired.
Trying to cover all scenarios in both leads to a lot of duplicated effort.
That said, I'm not saying redundant coverage is bad. It's often impractical to exercise all code via the browser, especially since in-browser tests are slower.
So comprehensive unit tests are often useful. I'm just searching for a practical balance between the two approaches.
The impact:
Each line must be carefully reviewed in hopes of catching all the mistakes.
We can’t assume anything works reliably, makes sense, does what it claims, or matches requirements.
We must question every line.
That’s a big problem.
Reviewing untrusted, poor quality code is time-consuming and demoralizing.
We're unlikely to catch all the problems.
It's impractical to "review our way to quality" when starting with code that's low quality, or solving the wrong problem.
Thankfully, most my career, my teams have worked with developers we could trust. But when we couldn't, PR’s required HUGE amounts of time, multiple rounds of comments, and occasionally, a complete rewrite.
✅ Keep state as local as possible. Start by declaring state in the component that uses it. Lift as needed.
✅ Store data that doesn't need to render in refs
✅ Minimize context usage
1/x...
✅ Avoid putting data that changes a lot in context
✅ Separate contexts based on when they change
✅ Place context providers as low as possible
✅ Memoize expensive operations via useMemo
✅ Avoid needless renders via React.memo
2/x...
✅ Put content that renders frequently in a separate component to minimize the amount that's rendered
✅ Split complex controlled forms into separate components
✅ Consider uncontrolled components for large, expensive forms
7 things that keep teams from doing Continuous Delivery (deploying daily or even hourly):
1. Non-atomic PRs.
Solution: Each PR must be ready for a prod deploy before it can be merged to `develop`. To separate deployment from release, use a feature flag.
1/x (thread)
2. Ad-hoc release notes.
Solution: Declare release notes in CHANGELOG.md. Require an entry in this file in each PR. Validate this file has changed on each CI build. This assures the release notes are customer friendly, accurate, and complete.
2/x
3. Flakey tests.
Solution: Most tests should be unit and integration tests. Mock the API. Simplify E2E tests. E2E should merely assure each section loads. Anything more granular may lead to flakiness due to changing data.
3/x