- Testing w/ backend API permission
-
read objectsThe API requires auth to access API. Without auth, nothing in API is accessible. - user auth setup - basic login/logout/username UI. Write a basic auth component
- What we want to achieve
- A unit that can manage login state
- not thinking about making an independent component, and how it interact w/ other component yet!
- Sketch the spec
- @input == props: ?
- UI user input == username, password
- UI event trigger == login button
- state (tided to UI view) == logged in username, login status
- data == token data <--- should I store this data in local var or in local state?
- @output == onChange when got token data <--- How does React handle event / bottom-up data flow? What is the best practice
- Current action planned
- Migrate current code into a new component.
- What's best practice of React project folder structure?
- Make the new component work with
App.tsx
- display login status
- Send auth data in request! Need to solve the
OPTION
problem.- DRF's issue page discussing the issue, mentioned the
django-cors-header
django app should solve the issue. This post gives some clues of how to setup client and server headers. - Important: Don't use Bearer! We are using jwt so use
JWT
instead! Also, when you don't have api login token yet, don't specify things in Authentication header, leave it empty. If you pass aAuthentication: JWT
, you'll be rejected by server.
- DRF's issue page discussing the issue, mentioned the
- create an object (by POST) w/ login status
- update/delete an object
- ⏸ establish login/logout mechanism
- ⏸ Store login data as global state --> learn redux!
- We found out that in order to build user auth, we will work on global login state, routing, and base navigation UI - all will be deeply coupled with and depend on the UI library - if we are using one. So, a better workflow might be to determine a UI library first (or build on our own from scratch). If we're using 3rd party library, we can have all the nav UI & routing support as well, hopefully the hook to user auth too. So we will move on and come back later.
- DONE. Fetch new action?
- Migrate current code into a new component.
- What we want to achieve
-
- Material UI: which library to use? Or build our own, or both? How about google's native mdc components?
- Besides links in the Reference section, can also refer to our previous project research result.
- What we need: nav bar, tab, icon, ...
- The Official MDC for React looks great, but indeed if some components not provided, you have to write an adapter w/ the MDC vanilla JS.
- There isn't any perfect one out there. So pick one, hopefully easy to customize, and start.
- Material UI vs MDC-React
- We're using MDC-React, but things like routing are not out of the box, probably less support than material UI, which has bigger community. (in terms of routing, actually material UI doesn't have that out of the box as well)
- MDC-React's visual effect is closer to mockup, which uses mdc's themer.
- Let's use MDC-React! And, we have to figure out react routing by ourselves. Time to get to work, dirty work!
We'll skip details of UI, just the functionality first!
- Select a library, build routing, build Navigation
- We're using MDC-React cuz closer to MDC design and so to our mockup.
- Use the top app bar (API doc, demo page). Build routing.
- react-router. We're using Typescript so will need its type as well.
- Some issue with typing for
react-router
, see this post to fix thecomponent
prop error that complaints... is not assignable to ...
.
- Some issue with typing for
- react-router. We're using Typescript so will need its type as well.
- We need some state management now:
<SocialAuth>
will login for us, but how can<HomePage>
get that logged in state and udpate its view? (conditionally, either landing page or user home page)
- Consider using Redux or context API to deal with global login status / state management.
- We can use Redux. Also for learning purpose. Watch out using w/ Typescript.
- Learn Redux basic
- Medium, 39K, Understanding Redux: The World’s Easiest Guide to Beginning Redux.
- Summarizing the 3 components of Redux. Fast and clear.
- Learn Redux + React (official). A compact one..
- Write reducer functions, define action objects.
- Initiate the store object, wrap your app in it.
- Connecting the store (global state) to a local component using
connect()
,mapStateToProps()
. This will "inject" part of the global state to your local component. - Read/write to global store in local component: read the state by accessing
this.props.yourStatePropName
; write by callingthis.props.dispatch(actionObject)
, and React should handle the view update for you.- Note that by writing action creator functions, it's a good way to write more succinct code. In short, write action (creator) function instead of object.
- Learn Redux basic
- We can use Redux. Also for learning purpose. Watch out using w/ Typescript.
- Learn Redux + React + TypeScript, official guide for typescript, or search google for more.
- Install dependencies:
npm i -S react-redux
,npm i -D @types/react-redux
- The official guide is not a good place to put together react, redux and typescript since it has to be un-opiniated. The github page for typing pattern is good, but it's more of best practice lookup, doesn't describe the flow to setup and the rationale behine each step. It'd be great if we can find a tutorial that puts these three together: react/redux/ts.
- Really good post & Archi.
- We can now read store. 1) implement
mapStateToProps()
, 2)connect()
. - Now how to write to store, i.e., dispatch? This will be done in
<SocialAuth />
.
- Install dependencies:
- Know how to use
mapDispatchToProps()
in Typescript.- Know what is
mapDispatchToProps
- Know how to use
mapDispatchToProps
in Typescript. (how to setup typings properly)
- Know what is
- Comply the "Container Component" convention / pattern. The SO post describe what it actually looks like (also the Medium post in the answer), and this post has some really good illustration, and a high level step-by-step guide to use Container Component pattern to facilitate usage of dispatch.
- Design our components and the structure.
- Draw a put-everything-together tech mockup
- Refactor root structure to reflect plan
- Top App Bar - put a logout button, make sure it works. Can use isLogin:true to debuf top app bar.
- Put a login button in landing page. Wire up update auth to redux store when logged in.
- Test logisn/logout and see if conditional rendering works!
React component planning - navigation and routed pages:
Challenge -10: How to setup routings, navigating programmaticlly and receiving parameters from route in React? So we can build master-detail views.
- Initialize page components for user app/add com/user com app page
- Add minimal necessary UI for navigation
- Add navigation transition between pages
-
Resolve issue when using redux with react-router. Also how to navigate progamically.
- What is
exact
in<Route>
- How to get
params
from route?- Router: define the route
<Route path="/to/your/page/:param/">
- Source page: either use
<Link to="/to/your/page/param-value-here/">
or<a href ...>
- To navigate programatically, or retreieve
params
, you need react-router's props "injected" into your component's prop first. There are three router props:location
,history
andmatch
, and after injected, you can access them in your component likethis.props.location
.this.props.history.push("to/your/page/param-vale/")
will let you navigate programatically.this.props.match.params["param-value"]
will let you retrieveparams
.
- To inject these props, first
import { RouteComponentProps, withRouter } from "react-router-dom";
withRouter
will inject router props to your component props. Wrap your reduxconnect()
in it and use it likeexport default withRouter(connect(...)(YourComponent));
.RouteComponentProps
is for your component's props type checking. There're many ways to setup, but basically, you can useRouteComponentProps
literally as type, this will give typings for the three router props:location
,history
andmatch
. To also do strict type checking onparams
, you can define aIParamsInterface
and use it likeRouteComponentProps<IParamsInterface>
. Then, theIParamsInterface
will do type checking onthis.props.match.params
.- To put this router props typing to your component's props, there're many way to do it:
- If you already have a component props interface, you can let it extend
RouteComponentProps<IParamsInterface>
. Or if your component don't need to retrieveparams
and just want the router props, then you can dointerface IMyComponentProps extends RouteComponentProps { ... }
- If your component doesn't have props interface, you can just use router props typing directly on the component like
class MyComponent<RouteComponentProps> extends React.Component {...}
.
- If you already have a component props interface, you can let it extend
- Router: define the route
- What is
-
Login/logout nav is done, but add company button we have some issues. Seems like it forces refresh, and so our global store is lost. Solution: change
href
toonClick
and navigate programatically.
-
Keywords: async action, api, request, data fetch + redux + typescript
Challenge -9: How to do async action in redux, such that redux works with fetch()
's promise and reflects http/api request's states, i.e., requesting
, success
and failure
? So that our app can use those states to show like loading spinners during wait.
- If action failed, how can we know and how to change the view to reflect the error state? If action takes a long time, how can we have an intermediate state and have the view change correspondedly?
First of all, there must be a place to 1) handle API request, e.g. a Promise that has .then()
and .catch()
block. Then there should be someone to 2) trigger updates on views, or, 2) trigger updates on state plus a 3) conditional rendering logic in view or side effects updating the view.
Three Aync Key Parts:
- API request and response handling
- Trigger state update
- State change reflects on view change, perhaps by conditional rendering
When considering redux in this setting, redux can handle state updating, and provide its state for conditional rendering. However, it does not do API request, so all the fetch()
, .then()
, .catch()
cannot be in redux. Also, it cannot do any side effects to udpate the view, say, navigate to another page, in any of reducer
and action
.
Putting this all together, first, as a transition from synchronous to asynchronous, one action now has to be extended to 3 or even more actions, to distinguish states like start request, request success, request fail (or timeout). In terms of store structure, it will at least add a new property e.g. status, that can store the async state = requesting, succeed, failed, ....
After some preliminary research, seems like we can use react-thunk
or react-saga
. But what the heck are they doing? Time for having a cup of coffee and read!
-
- Redux-Thunk: middleware for action creators. thunk --hijacks-- action creator --creates-- action --dispatches-- reducer --changes-- state/store. In terms of the async key parts mentioned above, the 1st step happens in the "thunk" aka middleware. 2nd step is the action creator & dispatch. 3rd step will be implement elsewhere in your component.
- Redux-Saga: a generator version of redux-thunk, and has some benefits over it. But also more complicated.
- *Article does not touch on step by step tutorial to use either of them.
-
Supplementary
-
Seems like saga is a popular choice, but is quite complicated.
- This quick small redux-saga tutorial sets up a in-depth yet quick saga example.
- This is redux-saga's official tutorial 🔥🔥
-
To test out saga, we do need a typescript setup. Including how to install typings.
npm i -S redux-saga
.- But seems like there're little tutorial about step by step setup for ts+redux-saga. We can just use
any
type as a work around as for now. As such, the saga official tutorial becomes quite valuable.
-
Start thinking about what actions & what reducers you need for API call.
- login/logout action? or combined as single action (but are their associated async action set the same)?
- associated API call actions basics - request, success, fail.
- Also a API call utility function.
- We have a REST API class now. Then, give another util for login/logout. (handle both social auth & backend auth)
- Then use these util func to write your update auth saga.
- But - are we going to write request/success/fail for all api actions in the future? Indeed it's repetitive and tedious. See this redux action routine package to automatically create those for you.
Challenge -7: How do we do route navigation programmatically in saga? We don't have access to history
in saga as react component does.
- OK, we finish sagas and now the login should work, theoretically. But, we use programatical navigation. How do we do navigation in saga?
connect-react-router
to the rescue. It basically connects router and redux, so you can access router history object to navigate from redux store.- Can follow this post to setup
connect-react-router
and, eventually, navigate in saga. - Debug: somehow
push()
is not working in saga. Based on this github issue, we are trying to downgrade from"connected-react-router": "^6.3.2",
to"connected-react-router": "6.0.0",
. --> doesn't help"react-router-dom": "^4.3.1",
upgrade to"5.0.0"
and see how that goes.- Just use
<Router>
instead of<ConnectedRouter>
? - Turns out it's the way we call
push
in our saga! Inspired by this post.
Instead of
...
put(push("/home/"));
yield put(SuccessAuth(authentication.state.userEmail, ""));
...
We should write
...
yield put(SuccessAuth(authentication.state.userEmail, ""));
yield put(push("/home/"));
...
yield [put(SuccessAuth(authentication.state.userEmail, "")), put(push("/home/"))];
won't work as well. See this post.
🎉🎉🎉
- Add logout POST to django server
-
Since we now need two sagas - one for logout and another for login, we need to have a root saga, and then branch out. There are several ways to include multiple sagas in the root. Common options are
all()
,fork()
,spawn()
, and a combination of them. See this RootSaga section of the redux-saga official for trade offs. We will use the simpliest case here, but as the app scale, we might want to switch tospawn()
. -
Login / logout using API w/ saga done!! 🎉 🎉 🎉 !!
Challenge -5: How to use try...catch...
block and fetch()
correctly, such that if error, we can handle side effects differently?
- Our previous try catch block in saga is not working. We don't handle
fetch
's then catch in the rest api object; instead, we just returnfetch()
, then the try catch block in saga will work. See this post. - Where to store api token? Because we want to handle try/catch in saga, we cannot
then()
in authentication object as well. The recommended way is to put that in global store, and access it only in saga. Of course, this means all API calls have to go through saga. --> Actually you can leave thethen()
in authentication, and just don't usecatch()
there. But yes, best practice still, access store in saga, and store is the only "single source of truth". See this post to get store in saga. (You have to create aselector
function)
Challenge -4: How to guard (protect) a route, such that if not authenticated (logged in), page will redirect to some page?
- Extra points - route gaurd for authentication.
- Turns out that by doing this, we actually don't need
push()
in saga anymore - because our redirection in public/private routes automatically does this.
- Turns out that by doing this, we actually don't need
Challenge -3: How can we reduce boilerplate for registering objects in redux's store, such that all related redux resources will be created in once, for 4 CRUD operations AND 3 async states each?
We learned how to create the whole redux stuff for an object, that is actions, reducer, saga and typings. Now we want to create these again, for all 12 situations - Create/Read/Update/Delete AND Requesting/Success/Failure for each CRUD. If you observe, you'll notice many things are repetitive. E.g., Requesting
's action only cares about updating async state
, but not doing anything related to our object. Failure
's action only cares about error
message and async state
. If we hard code for all 12 situations, it will be a bulky huge pile of code with tons of copy pasting. Plus, we need to do all this over again whenever we want to add new data model. If we need two models in redux, that'd be 24. If four models, you have 48 situations to write their actions, plus reducer and saga. If you want to introduce one more async state, say, Triggered
, you have 64 situations. You get it. You'll easily end up writing 100+ functions. The boilerplate problem is serious.
Challenge -2: Given a data model, how can we build a factory to generate actions, typings, reducers and sagas all in once?
- Think about next steps - "CRUD" & forms to create com & app & status
- Stepping into Formik. Install
npm install formik --save
. - Install input fields from mdc-react:
npm install @material/react-text-field
. - Also prepare to material icon!
- Stepping into Formik. Install
- Plan how to create UI for firing REST API request.
- Rest request for company - POST/DELETE/PATCH/GET, how to create async action & saga efficiently? Copy paste requested/success/failure?
redux-arc, star=149
can be a choice for RESTful api async actions w/ redux.redux-rest, star=179
redux-rest-resources, star=179
redux-api, star=489
- Design a state action creator for RESTful API
Continue to setup CRUD interface for each area of the app. Notice the relationship needs special care, and require back and forth between frontend form and backend serializer and viewset.
- Make a plan for the crud UI.
-
User Application Page:
- company list
- (first few application, or application w/o status)
- company list
-
Add Company Page:
- a form to create company
-
User Company Application Page:
- A company
- All applications associate with it
- a form to create application
- create data model in react first
- create application
- create all related field's data model as well
- add 1-2 required form field
- try to send it & create the object on server. check in database.
- create data model in react first
- a form to create application status
-
Profile Page
- (logged in user's information)
-
Challenge 1: How can we build a form factory that generates form for different data models? Validation and serialization also relationships should be handled as well!
As we start building forms for different models, and also adding in more fields, we found ourselves copy pasting so badly, and the Formik form is getting very long with hard-coded HTML fields and inputs (over 200 LOC).
Challenge 2: How can we refactor for Django serializer such that we don't have to keep writing similar-logic def create()
? We want writable relationship fields handled as well.
One frequent need is to supply current authenticated user to the object's user field. Another is one-to-one relationship. These two have to be manually assigned upon object creation.
In contrast, foreign key field, or Many-to-One field, can be automatically validated and supplied by adding a the_exact_fieldname_as_model = PrimaryKeyRelatedField(read_only=False, ...)
. This assumes the frontend supplies the foreign key field by the object id.
Goals
- Refactor React form code: it's better to write components that modularize:
- 🔥 🔥 🔥 form fields. modularize integer/text/onetoone... field
- form submit. once you have that modular field, it's easier to write the submit & error validation logic.
- Refactor Django form code:
- Modularize the process to set one to one relationship on CRUD operation
- Perhaps a mixin might be a good choice.
- Modularize the process to set one to one relationship on CRUD operation
Getting Started
So, what are the key parts of a form? (In React frontend):
// <Formik> props:
const initialValues = { field_name: ...}
const validate = (values: typeof initialValues) => {error checks...} => errors
const onSubmit = (values, ...) => {
// create relational objects
// create main objects
// dispatch
// if needed, set a callback function for more side effects e.g. redirect
}
// things inside <Formik>:
//
// <Textfield> from mdc-react; props:
const label = "..."
const trailingIcon? = "..."
// <Input> from mdc-react; props:
const name = "field_name; <input>'s name for form"
const inputType = "input|textarea"
// error message
{errors.field_name && touched.field_name && errors.field_name}
So, what's the input needed? Anything else can be hard coded:
⚠️ field_name
, and its initial value- maybe we can use data model class to help us?
⚠️ instruction/info about which field is relational, and how relational objects should be created.- This might map to DRF's seriailizer? Or Django's model form.
- Optional
callback
after submit & store state changed. label
text for field, for human read.- Optional
trailingIcon
, can just pass inicon="material-icon-alias"
, assumed we are using material icons. inputType
, but this can be supplied byinput
as default value first since most of the time we will only useinput
.- We do need a
input
for<Input>
, so we can do email, password, ... - ️️️️
⚠️ Instructions on how to validate. We need:- A function returns boolean
- Error message
<Formik
initialValues="{...}"
validate="Func"
onSubmit="Func"
>
{({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting
}) => {
<Form>
<TextField
label="string"
onTrailingIconSelect="Func"
trailingIcon="Func"
>
<Input
name="field_name"
inputType="input | textarea"
onChange="formik func"
onBlur="formik func"
value="formik value: values.field_name"
/>
</TextField>
{
errors.field_name && /* formik errors */
touched.field_name && /* formik touched */
errors.field_name
}
...
<Button
type="submit"
disabled={isSubmitting}
unelevated
children="submit button text"
/>
<Button
onClick="Func"
unelevated
children="button text"
/>
...
</Form>
}}
</Formik>
FormFactory Component
interface IFormFactoryProps {
initialValues: DataModel
validate: (values: FormikValues) => FormikErrors<FormikValues>
onSubmit: (values: FormikValues, { setSubmitting } : { setSubmitting: Function }) => void
}
<FormFactory {...props} />
FormFieldFactory Component
enum TrailingIconEffect {
CLEAR_FIELD
}
interface IFormFieldFactoryProps {
field_name: string
label: string
trailingIconEffect: TrailingIconEffect
icon: string
/* formik */
formikOnChange
formikOnBlur
formikValues
formilErrors
formilTouched
}
<FormFieldFactory {...props} />
<TextField
label="string"
onTrailingIconSelect="Func" <-- trailingIconEffect
trailingIcon="Func" <-- trailingIconEffect
>
<Input
name="field_name"
inputType="input | textarea"
onChange="formik func"
onBlur="formik func"
value="formik value: values.field_name"
/>
</TextField>
{
errors.field_name && /* formik errors */
touched.field_name && /* formik touched */
errors.field_name
}
✅ fixed bug - in factory function reducer: didn't actually distinguish different object names. So every action will apply to all reducers, and thereby cause object creation in every object stores.
Also got stuck very long time on combineReducer
, router
, reducer and action type. This post effectively solves it and gives a lesson: not specifying specific action type on reducer's arg seems like a pattern that one should follow. Can we verify this, however?
We will not break any existing forms, but will use "application status" as an example to create form by our factory. But first of all, we need to list all the applications.
- List all applications
- create a application component accepting props
- in
user-com-app-page.tsx
: use the application component to list all applications. - How to limit the application to only that company's applications? Need to do a filter query on REST API.
- Ans: do it in React. Just filter the application list where
user_company === company.uuid
.
- Ans: do it in React. Just filter the application list where
- optimize: move
listApplication
to user app page so we only have to fire it once.- Perhpas better to put list application & company in auth saga, so upon login the app will only fetch once. if put in user app page lifecycle, it will fetch upon every navigation
-
Before moving forward, let's do a delete feature as well!
- Need to use Django Signal to delete all the related one to one field when model is deleted. The
on_delete
is not enough.
- Need to use Django Signal to delete all the related one to one field when model is deleted. The
-
Upon logout, we need to clear out the store.
-
Permission control IN DRF
- We kind of achieved it, but we need to refactor and clean up uncecessary code.
- Notice: the
get_queryset()
on viewset only take effect on listing methods, i.e., when the endpoint is "returning a list of objects". For single-object endpoint request likeGET/DELETE/...
, we have to find other ways, like writing a custom permission class as the occifial document suggests. - The list objects permission is working, and we can write that into a viewset mixin.
- But we still need to work on single object permission. After implementing it, test by doing a DELETE in frontend.
- Prepare a space to ready to put application status form
- test out the form factory and create the applciation status form.
If everything goes right, then...
-
Consider refactor backend serializer mixin
-
You want to double check application status model's field - esp the
text
field.- What do u want to achieve? Reusable status type.
- We want to leave it for user to define their own status. But, in the future, we may implement sth like "suggested words" based on history status text input.
-
(You might want to refactor frontend form validator and modularize them)
- We can push back validator, and complete all fields first. Also it'll be more clear what kind of field types we have and need what kinds of validator.
-
(In order to let trailingIconSelect to work and clear the field, we might need to get the
ref
of<Input>
inform-field-factory.tsx
, i.e., manually change the input element value. This should also trigger theonChange
event so Formik's value is also updated)- It's better to just not use trailing icon functionality at this point. It will also use up a tab hit as well so, not very useful feature.
-
test out the mixin to enable relational field write operation in serializer and view.
-
Put everything together: test out the application status form and check in database.
- Especially, complete all fields
- But we have to refactor previous forms first, so it'll be easier to augment fields to them.
- We will leave date validation as TODO.
- We need to make our frontend URL validator stricter, since our backend URLfield is pretty strict, and the API call will fail.
- Especially, complete all fields
-
List out app statuses
-
...and status links
-
Checkpoint: deploy to production server and test!
-
Some bug - when new status created, frontend list didn't show links. But when you login again, they are their now.
-
Bug - after form submit - clicked "Create", the form did not close.
-
TODO issues: some server error does not reflect on React saga and will just fail silently. Example:
- If login fails, sometimes react will still navigate to internal pages.
- OK, we kind of try to fix this, by manually checking server res.status to detect status code larger than 400.
- When permission denied, e.g., when performing a delete, will fail silently & even redux will think the obj is deleted and reflect on frontend, but in fact backend database does not perform such deletion.
- We tried to fix this by intercepting the "user attribute not on obj" error when checking obj level permission.
- If login fails, sometimes react will still navigate to internal pages.
-
Add update/delete feature, to all: company, application, and application status.
- delete application status
- update company|app|app status|app status link
- update company
- Let's stop here - and see what is required to enable a data model to do update operation:
- Form has to distinguish between create & update. This is usually done by the form component accepting an optional model object, say, use form's props to pass in the model object. If passed in, then treat the form as an update; otherwise, it's a form for creation.
- Initial value needs conditional assignment; either using the provided model object, or use a default value.
- Submit button text has to change: either
Update
orCreate
. - Submit function has to change: either dispatching a createAction, or a updateAction.
- Add udpateAction to
mapDispatchToProps
.
- Add udpateAction to
- The component that accomodates form is responsible for providing the model object to update. In case of company, the
add-com-page
component is responsible for fetching the company object. Here,add-com-page
obtain the company by trying to fetch theuuid
of a company from its route params.- There's a chain of passing company object here: form props expects company object -->
add-com-page
expects uuid from its route params --> there's a edit button on<CompanyComponent>
, and its onClick will navigate route toadd-com-page
w/ the company uuid in the url.
- There's a chain of passing company object here: form props expects company object -->
- The start point of update operation is a button click, most likely from the edit button's onClick callback. How to propogate model object info from there, to the form, is the main goal here.
- Form has to distinguish between create & update. This is usually done by the form component accepting an optional model object, say, use form's props to pass in the model object. If passed in, then treat the form as an update; otherwise, it's a form for creation.
-
Add update operation to the following data models:
- update application
- The edit button: most likely in the model's component class. Here its
<ApplicationComponent>
. - How to propogate to form component:
- The hierarchy structure:
<ApplicationComponent>
--> ... --><ApplicationFormComponent>
- The form is not necessarily in the model component. It's ok to have model component and form be separated components, and it should be. It's just our passing object chain is not trivial and not propogating downward to child, because here, model component & form are siblings. We might want to change this in the future, but under existing hierarchy, we need to pass obj info one level upward, then pass to form.
- One way to do it:
<ApplicationComponent>
delegate onClick to its parent by expososing in props, and pass the app object by callback args.<CompanyApplicationComponent>
is the parent and implements the onClick, & accept the app object. However, it does not accomodates form component.user-com-app-page
does.- Maybe we should refactor this structure. It seems like the best practice is to let form and the displaying component be as close as to each other, so it's easier for form to obtain the object. Some of people even merge form and display into one component, and use conditional rendering to handle read/create/update.
- The hierarchy structure:
- The edit button: most likely in the model's component class. Here its
- update application
- We need a review of current hierarchy first. Draw a diagram for this.
- Now make changes to the diagram, and let form be closer to display component.
- 🔥 🔥 🔥Implement the change
-
update appplication status
-
update application status link
-
(Refining all fields on data models)
-
Let tab work in user app page. You may want to decide whether label or status should be the tabs. No matter which one, it should be a fixed or stable amount.
-
And stop ... reflection on next steps and roadmaps.
- user permission feature. Now user will get all companies from the database.
- Have to enable google console "allow all user even outside of orgs"
- user permission feature. Now user will get all companies from the database.
-
Polish frontend layout.
-
Previous TODO not fulfilled?
Repos
Production sites
Tools
- Material Icons
- Official MDC for React
- How to write VScode snippet
- React Router quick reference to match, location and history
Technologies
-
Use typescript with React while using
create-react-app
to scaffold the project. -
Deploy repo to github as a live website using gh-pages
- If you got a 404 page, and you made sure your homepage url is correct in
package.json
, chances are the page is updating so may need some time reflecting the new deployment. In short: be faithful & patient!
- If you got a 404 page, and you made sure your homepage url is correct in
-
Learnings & Readings
- Generic
- Declarative vs Imperative Programming
- 5 Tools for Faster Development in React: project management tools, devTools, ...
- Coding Style
- FB Official Doc: React Project's File Structure: "Don’t overthink it".
- How To Write Better Code in React: Share and reuse components, propTypes , Know when to make new components
- Ways to do things
- Where to Hold React Component Data: state, store, static, and this: local state, redux, ...
- Is it bad practice to nest states in React?. Should I nest state? Best practice to design state structure?
- What is the equivalence of Angular @output, @input == onChange trigger, props in React?
- Good quality UI libraries
- 11 React UI Component Libraries you Should Know in 2019: React Material-UI, Material Components Web, ...
- Reflections
- A good way to get to know React is to compare Angular to its equivalance in React.
- @input and @output
- Service <---> state management?
- A good way to get to know React is to compare Angular to its equivalance in React.
- Generic
Caveats & Troubleshooting
- Interface optional callback dilemma. See SO: Declare optional Event callback in Typescript React for solution.