|
@@ -2,3 +2,165 @@
|
|
|
|
|
|
[Description](http://doc.a-level.com.ua/react-layers)
|
|
|
|
|
|
+# React Layers
|
|
|
+**NIH-syndrom again**
|
|
|
+
|
|
|
+## Goal
|
|
|
+
|
|
|
+Create layered **React** microframework with reasonable architecture and minimal
|
|
|
+boilerplate.
|
|
|
+
|
|
|
+## Architecture
|
|
|
+
|
|
|
+Layers, one-by-one from deepest layer (global state management, server queries) to directly **View** (react) layer:
|
|
|
+
|
|
|
+### Pub (and Sub)
|
|
|
+
|
|
|
+Minimalistic pub/sub with recursive support builded on top of [Javascript Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
|
|
|
+```javascript
|
|
|
+const pub = createPub()
|
|
|
+pub.subscribe((state, key, oldValue, newValue) => console.log(state, key, oldValue, newValue))
|
|
|
+
|
|
|
+pub.a = 5
|
|
|
+pub.arr = ['foo', 'bar']
|
|
|
+pub.arr[1] = 'baz'
|
|
|
+```
|
|
|
+
|
|
|
+On any change of keys in objects `subscriber` called. You can listen to updates in any nested object. Listening on upper level hears also nested updates.
|
|
|
+This way you can achieve global state tree without boilerplate, but this way requires attention to what you do with state.
|
|
|
+
|
|
|
+#### React Listening Hook
|
|
|
+
|
|
|
+`usePub`, obviously. Causes react component update on changing of pub. To minimize overhead, `usePub` can be used for nested pubs, instead of root state.
|
|
|
+
|
|
|
+```javascript
|
|
|
+const arrPub = usePub(pub.arr)
|
|
|
+```
|
|
|
+
|
|
|
+Hook returns pub, so `arrPub === pub.arr`, but component updating when pub changes.
|
|
|
+
|
|
|
+#### Network Requests/Promises
|
|
|
+
|
|
|
+**Pub** can be easily used for tracking promises.
|
|
|
+!!! (error)
|
|
|
+Be careful, **storing promises in global state causes memory leaks**
|
|
|
+!!!
|
|
|
+
|
|
|
+##### How
|
|
|
+0. creating nested **Pub** with structure like
|
|
|
+```javascript
|
|
|
+{
|
|
|
+ promiseName1: {status, payload, error}
|
|
|
+ promiseName2: {status, payload, error}
|
|
|
+}
|
|
|
+```
|
|
|
+2. Writing function, which have `promiseName` and exactly promise as argument and updates promise pub branch when promise updating its state (**PENDING => FULFILLED** or
|
|
|
+**PENDING => REJECTED**), storing `status`, `payload` and `error` in promise pub named branch
|
|
|
+3. ~~rtk-query~~:
|
|
|
+4.
|
|
|
+
|
|
|
+ - composing functions: query function, which returns promise and "thunk", which passed promise life through pub. Something like `createAsyncThunk`, or `createQuery`, with next parameters:
|
|
|
+ - `promiseName`.
|
|
|
+ - `queryFunc` - function, which returns promises.
|
|
|
+
|
|
|
+
|
|
|
+ or even **carrying**:
|
|
|
+ 1. `createQueryPub` creates pub with promises, some basic options passed (like cache timeout and probably queryFunc) and returns `createQuery` or object with functions from step 3 if queryFunc passed on first step into createQueryPub
|
|
|
+ 2. `createQuery(queryFunc)` returns function, which:
|
|
|
+ 3. receives `promiseName`, returns object with functions:
|
|
|
+ - `query`, which accepts arguments and passes it to `queryFunc`
|
|
|
+ - `usePromiseNameQuery`/`usePromiseNameMutation` like in `rtk-query`
|
|
|
+ - and so... (no ideas right now, but something about atomic actions/force query/invalidate cache)
|
|
|
+
|
|
|
+
|
|
|
+### Actions/Controllers/Thunks
|
|
|
+To avoid random messy access to global state, some functions which works with are welcome, but they aren't necessary.
|
|
|
+
|
|
|
+### Routing
|
|
|
+Instead of moving Routing on React level, making routing ~~great again~~ separated from React Markup.
|
|
|
+
|
|
|
+#### Why
|
|
|
+- _Because_
|
|
|
+- Routing describes _page_ content overall, but not how it will exactly looks (React does)
|
|
|
+- Routing describes **request** to backend, regardless of look.
|
|
|
+
|
|
|
+
|
|
|
+#### Two layers of routing:
|
|
|
+- address routing
|
|
|
+- hash routing
|
|
|
+
|
|
|
+#### Conclusion
|
|
|
+Routing configuration should be separated from React markup, and store next information:
|
|
|
+- **Route Template** like `/page/:id`
|
|
|
+- **Route Name** which probably same as one of promise names
|
|
|
+- **Query Func** or even **Query Params** to make query with **Pub**. **Query Func** can be made very easy with **GraphQL**. **Query Func** gets
|
|
|
+ all information about route (or, at least, params from route) as function parameters to make query. Usually **Query Func** is just thunk with promise for Pub.
|
|
|
+- **React Page Component**, which should be rendered for this **Route**. Probably with **MetaPlate** to avoid _props drilling, `useHuy`, `map`, props waterfall, `usePizda`, `map`_
|
|
|
+ and so
|
|
|
+- **Some private routing configuration**, like `fallback` (address to go, if private route not accessible) and `roles` - list of user roles, which can
|
|
|
+ visit this private page. Some HOC needed to provide current user roles list (usually from global state/jwt) to be matched with `roles` props
|
|
|
+
|
|
|
+#### some ideas and thoughts
|
|
|
+- address routing (from domain till ?
|
|
|
+- hash routing (from # till end of address)
|
|
|
+
|
|
|
+
|
|
|
+possible routing configuration:
|
|
|
+- list style, as JSON or JSX (like <Route .../>)
|
|
|
+- tree style, potentially faster (but it doesn't matters on client side)
|
|
|
+```javascript
|
|
|
+const treeRouteConfig = {
|
|
|
+ //root page:
|
|
|
+ "/": PageMain,
|
|
|
+ //login
|
|
|
+ '/login': PageLogin,
|
|
|
+ //admin part with subroutes
|
|
|
+ '/admin': {
|
|
|
+ "/": PageAdminMain, // /admin/
|
|
|
+ "/users": PageAdminUsers, // /admin/users
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+A lot of questions at the moment about three configs:
|
|
|
+what if route params AND nested routes
|
|
|
+?-params and hash layer - where is should be configured? looks like this configuration should be moved into page component, not to be in tree.
|
|
|
+so cancel tree idea for now
|
|
|
+
|
|
|
+
|
|
|
+### View Layer (react)
|
|
|
+_Mostly_ template react components without boilerplate, hookafucking, billions of `useEffect` and so. **Styling** moved to **CSS** or... not.
|
|
|
+
|
|
|
+## Tools
|
|
|
+- `createPub` and `qjp` (should be refucktored) from `v01`
|
|
|
+- Some wrapper around `react-router-dom` to pass query func to `Route` component or other configuration
|
|
|
+- **Metaplate**, which provides meta templating abilities to write nesting components page layout, according to nested query data without boilerplate and map
|
|
|
+- Something for **SSR**
|
|
|
+
|
|
|
+## Additionals
|
|
|
+Some addons
|
|
|
+### SSR
|
|
|
+For now no implementations about **SSR**, but there are some _ideas_:
|
|
|
+- CSSR. **Client Server Side Rendering** lol. Draw HTML in user browser, then upload it back to backend. Pros and cons:
|
|
|
+ - Pros:
|
|
|
+ - no need of backend SSR code
|
|
|
+ - no load on server
|
|
|
+ - Cons:
|
|
|
+ - client-side load
|
|
|
+ - client can scam
|
|
|
+ - no SEO HTML before user comes to page
|
|
|
+- **Simplicity**. No backend code with all this **SSR** boilerplate. Using maximal isomorphy as it possible.
|
|
|
+ - **Components** may have **stub** version.
|
|
|
+ - **DOM-Elements** and/or **Components** may be marked as **private** for sensual information. This **DOM-Elements** or **Components** should
|
|
|
+ not be rendered on backend (only stub rendered)
|
|
|
+ - Marking components/elements as **private** gives ability to get seamless **SSR** without ~~almost~~ coding on backend.
|
|
|
+
|
|
|
+
|
|
|
+## TODO
|
|
|
+- **Check speed** or **MetaPlate** on huge updates.
|
|
|
+- **Wrapper** around **react-router-dom**.
|
|
|
+- **SSR** library
|
|
|
+
|
|
|
+
|