This is a playground to try new React practices and libraries. Its nightmarish End of Life is my inevitable burnout.
It is also an opportunity to shamelessly shill stupid narratives I write on a website powered by this codebase. That's m-u-r-d-e-r-w-i-t-h-f-r-i-e-n-d-s-dot-parrrttaaayyy.
Additional shilling: Ask a Dev, your friendly neighborhood programmers hosting free, open office hours.
This is an excercise in synthesis from a community that languishes over an unopinionated framework. React's proliferation and accessibilty may ultimately be its doom. Until then, like everyone else on Medium and their mother, I'm documenting and making this up as I go.
Say hey in Issues if you wanna dance.
What is a developer without a code?
A universal component is a shared UI element used across multiple slices. For example, a <Button />
. These are referenced +dumb/<Name>
. They do not retrieve data and are not tied to any state management.
A connected component is almost always enhanced by Apollo. The graphql
or compose
call is invoked in the index.js
file with all related logic (recompose
HOCs, memoized functions, whatever) while the React component is in presenter
. Raised as an old-fashioned Rails boy, I sleep easy at night knowing param logic is separated from the view.
Filename | Description |
---|---|
remote.graphql | Pushes and pulls everything from the server. Contains query mutation and subscription operation types. |
presenter.(j|t)sx? | Strictly presentational. |
index.js | Automatically imported by whatever parent component, this wraps the Presenter in Apollo logic |
style.scss | SCSS duh |
- Only
index.js
files can fetch data. - Stateless Functional Components are avoided like the plague. There's always war in Eurasia, and there have always been promises of SFC performance optimizations. The war - and the wait - continue.
- While we're picking bones, FACCs only make sense to their author and are also 86'd. I understand this as a growing pattern, but deriving the purpose of a complex FACC is not worth the initial convenience. Grotesque nesting is far more easily achieved when you're starting an extra indent deep, and I'm old enough to remember callback hell.
Also, TODOMoResearch, but this pattern seemingly breaks the downstream equality checks reducing its performance, right?If the child component is a PureComponent, it will have reduced performance. Render props are good. - App state logic is also separated to clear an open path to component testing. While it'd be more convenient to include
graphql
orcompose
along with the rest of presenter, this makes decoupling a bigger headache in the future when The Next Greatest State Manager/You Don't Need a State Manager philosophy rises to prominence.
A slice is a division that operates independent of another division of the app. A slice does not access another slice, as this would defeat the purpose. Each slice can be composed of:
File/Folder Name | Description |
---|---|
routes.js | Dynamic routing to pages listed such as Index , Show |
_components/ | Directory of "private" components only used within the slice |
<Route>/ | Routes for the slice, such as Index or Show |
- Components in the
_components/
directory do not have to be "dumb." This word "dumb" is thrown around recklessly in the React ecosystem. Here, it means "this component does not connect to a server." You may consider this reckless, but I disagree with Dan's binding data only to containers. That adds an order of complexity to interpreting the codebase (instead, state management connection/data fetching inindex.js
on a HOC and keeping the logic separate). These components can still be connected to a state manager, they just can't do any data fetching on their own. They can write [remote] data. Manipulating props is more efficient on leaves (React doesn't have to recompute every time pass through props are changed). - Components in the
_components/
directory definitely can't be used outside theirslice
. - Wether to organize with a modules or components/features/ducks structure has been well-debated; I believe using a separate modules directory adds more complexity, uncolocated logic, and less encapsulation, therefore, it's an antipattern.
- Nesting folders within
/slices
more than three levels deep will result in certain death. - Runner-up names for
Slice
included "Domain", "Fence", "Scene," "Molecule," "Fragment," "Neighborhood," but nothing got the point across as efficiently and without metaphor. You've probably already imagined a metaphor as a slice of pie. Well done. But don't call it cute. - Yes, I have built many bikesheds, why do you ask?
The data model is defined in server/types
, serverless functions and some validation in server/functions
, and permissions (a WIP) in server/permissions
. Feels good to be in a relationship [mindset] again.
- GraphQL configuration is an uphill battle that's well worth it. Mentally evaluating data models via a Graph file is fun.
- HOCs are the truth, but man do they suck to debug with React Dev Tools. Apollo warmly embraces everyone in a
<Mutation>
or<Query>
. This is a curmudgeonly, geriatric statement, but bad (nested) JSX is gross XML. I hate it. I love it. I hate it again. To reduce the cognitive load of flip-flopping, the dev experience uses a more functional syntax to bind data. - Graphcool is following their own spec. No
implements
.@isUnique
not@unique
. No aliasing of resolvers. People much smarter than me have made these decisions, but the confusion is palpable. That said, Graph is still enjoying infancy. - It was a mistake to use Graphcool when they've clearly bet the farm on Prisma, but I wanted to prototype this in GraphQL before deep diving into devops. The "playground" / cloud IDE, the out-of-the-box subscription/simple/relay endpoints, and the examples of all the right things are just good 'ol fashioned, quality dev relations. Pain points: logging is impossible and functions/resolvers are poorly documented.
CSS is fine. SCSS is great. Styled components only add bloat to existing JS files and this whole template literal business is a mess. Styles are imported via CSS modules and precompiled with SCSS. Just because it ends in Sheets not Script doesn't mean it can't play in the sandbox too.
- That said, I'm so over that BEM/BOOM/BUM rubbish. In all cases,
.root
is used instead of a cheeky name to define the component's archclassName
. - I will lose zero sleep over styling tags. There's an art to the cascade. Encapsulation may be a core tenant of React-land, but IMHO CSS is a different zip code.
- CSS is the easiest language to write and the hardest to write well.
For consistency across the app, property names should be easily guessable.
Property | Function | Example |
---|---|---|
displayName |
Identifying, consumable title | The Hat Pie Company |
text |
Long-winded diatribe or instance's core purpose | We're the best cobblers this side of the Mississippi, that's what we do, don't let the name fool you |
Booleans | Booleans are preceeded by is , has , does , or should |
isPie hasHats doesMakeShoes shouldRebrand |
id |
Unique identifier | 1 |
In order of influence
- Loading data components/dynamic routing
- The 100% correct way
- Features pattern
- Scenes pattern with smart/dumb elements
- Adventures in TypeScript
- Storybook
- Forward Refs
- Graphcool and Apollo (GraphQL)
- Plop pllloooppppppppp
Framework | Description | Additional Snark |
---|---|---|
Firebase | A non-relational database requiring a sacrifice of sanity. | Real-time is dope. Authentication is tear-free. Great service to scrap your way through a prototype. Impressive speed, especially to a Muggle (inconceivably fast). The headache (12:30; this man's humor must be preserved for future generations) of crafting complex security rules to avoid nested access and maintaining the same data in multiple references invalidates all pros. |
- Thoughtful codesplitting with Loadable
- Jest/Enzyme testing
- Unified conversion to TypeScript.
.d.ts
or.tsx
just pick one. - Soothing ESLint
- Use React Context instead of Apollo's local hackery
Confront the sins of+dumb/Inputs
- Advanced Storybook usage
$ yarn install && yarn start
Hit up http://localhost:8080.
Invoked $ yarn <cmd>
command | description |
---|---|
start | Run dev server with HMR at http://localhost:8080. Uses webpack/development.js |
build | Compile for production into dist/. Uses webpack/production.js |
storybook | See +dumb component library by running a server at http://localhost:9001 |
storybook:build | Compile storybook to dist-storybook/ |
plop | Compose a new addition to the code base from a template |
Skirt relative path hell with an +<alias>
. Found in webpack/shared
and tsconfig.json
.
prefix | path |
---|---|
+dumb |
src/universal/dumb |
+root |
src |