-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/normalizr/add country entity and students that belongs to contry #36
base: feature/normalizer_implementation
Are you sure you want to change the base?
Conversation
|
||
public constructor() { | ||
this.id = -1; | ||
this.gotActiveTraining = false; | ||
this.fullname = ""; | ||
this.email = ""; | ||
this.countryId = -1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-1
smells bad to me. Why not null
or undefined
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think null can be a good value, on the other hand is it a good idea to define this as a class or should it be an interface? Should we add a factory to initialize a new instance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we are using this value because Uncontrolled input warning (If you initialize input value to null, it means that is uncontrolled component) But I think that this initialization has to be component liability and it doesn't delegate to class constructor used in reducer default value state. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, if it's a component problem, let the component to deal with it.
On the other hand, I would use an interface too. I avoid factory methods unless you need to share some initialization logic that I don't see on this case.
return { | ||
id: country.id, | ||
name: country.name | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need a new object? Is not just an interface? In TS, any Country is a CountryView because of structural typing, so just use the original object, even when it has extra props, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, we can return original object and in the future, map additional props or some format
@@ -7,7 +7,11 @@ class StudentMapper { | |||
id: student.id, | |||
gotActiveTraining: student.gotActiveTraining, | |||
fullname: student.fullname, | |||
email: student.email | |||
email: student.email, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make trailing comma a convention to avoid this in PRs. It can be set up in TSLint.
email: student.email, | ||
country: { | ||
id: student.countryId, | ||
name: '' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems wrong (before looking more code).
|
||
public constructor() { | ||
this.id = -1; | ||
this.gotActiveTraining = false; | ||
this.fullname = ""; | ||
this.email = ""; | ||
this.country = new CountryView(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also seems wrong. Either leave country undefined, or use a CountryView.Empty like with the Null Object pattern. Otherwise it seems you're creating a brand new country for each student.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, or we could assign a default country entry. Thinking now about pros adding a factory to create then StudentView
export const getCountries = (state) : CountryView[] => { | ||
const ids = getIds(state.countryDomain.allIds); | ||
return ids.map(id => getCountry(state, id)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why getCountry and getCountries are on different places?
return state; | ||
} | ||
|
||
export const getIds = (state: number[]) => state; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably getIds is not needed.
export const getStudent = (state, id) : StudentView => { | ||
return { | ||
...state.studentDomain.byId[id], | ||
country: getCountry(state, state.studentDomain.byId[id].country) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got lost: previously state was the dictionary of students. Now it's the global state. This seems hard to manage unless you strong-type arguments to prevent mismatches.
@@ -0,0 +1,4 @@ | |||
import { schema } from 'normalizr'; | |||
|
|||
export const countrySchema = new schema.Entity('countries'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should put all schemas on the same file, so you can export without the schema prefix, like mySchema.country
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just in case it scales we could go for a solution like
+ src
+ schema
* index.ts
* country.ts
* student.ts
Then just in index we export all the schemas.
If somebody has to use this he only has to worry about
import "./schema"
But on the name of the schema it self, not sure if we should keep the "Schema" prefix, to avoid creating aliases if there are name collisions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, the index would be a solution in case we need to break down. Btw I meant suffix, not prefix.
import { countrySchema } from './countrySchema'; | ||
|
||
export const studentSchema = new schema.Entity('students', { | ||
country: countrySchema |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not true. Current API returns countryId, not a nested object. What I have understood so far is that this line would be helpful to denormalize country information from students, but this is not going to happen. If student gets a country object just for the UI, it doesn't apply here, and probably we would like to stop adding that country to the student object, instead you can look up the country from the Id when needed. Thumbs down to normalizr here, to extract nested object, I see value on it to convert countries array to a dictionary.
allIds | ||
}); | ||
|
||
export const getCountries = (state) : CountryView[] => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getCountries(state)
This state should be already countryDomain
@@ -6,5 +6,6 @@ export const actionsEnums = { | |||
STUDENT_FIELD_VALUE_CHANGED: "STUDENT_FIELD_VALUE_CHANGED", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can remove this action is covered by a thunk function
<div className={wrapperClass}> | ||
<label htmlFor={this.props.name}> | ||
{this.props.label} | ||
</label> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This label should not be included in this component
No description provided.