Skip to content

Conversation

@IvanScoles
Copy link
Collaborator

@IvanScoles IvanScoles commented Jul 19, 2018

Higher order components

  • Login form as HOC
  • Main form from LoginPage integrated with HOC from Login
  • Top form from NavBar integrated with HOC from Login
  • Navbar integrated with login page

Ready to review

Description

Here we have 2 forms with the same structure from form data username and password, to manage the same setState and use the same action from login (HOC). But we can see two ways to show the form information LoginFormMain and LoginFormTop both wrapper by the HOC

hoc_main_form

hoc_login_top

Emanuel Pereyra and others added 4 commits July 17, 2018 18:17
Removed lodash omit from components
fixed a bug un userActions tests
changed actions with more than 1 parameter to payload
created utils functions to replace lodash utilites
static propTypes = {
username: PropTypes.string.isRequired,
password: PropTypes.string.isRequired,
handleOnChange: PropTypes.func.isRequired,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleOnChange should not be required, rather a default prop value with a noop

static defaultProps = {
    handleOnChange: () => {}
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

username: PropTypes.string.isRequired,
password: PropTypes.string.isRequired,
handleOnChange: PropTypes.func.isRequired,
handleOnSubmit: PropTypes.func.isRequired

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleOnSubmit should not be required, rather a default prop value with a noop

static defaultProps = {
    handleOnSubmit: () => {}
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

const LoginHOC = (LoginChild) => {
class LoginParent extends React.Component {
static propTypes = {
actions: PropTypes.object.isRequired
Copy link

@javier-pepe javier-pepe Jul 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could use PropTypes.shape instead of PropTypes.object

actions: PropTypes.shape({
    foo: PropTypes.func,
    bar: PropTypes.func
})

actions should not be a required prop, maybe using defaults with no operations for expected actions

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, good practice to do.


const { username, password } = this.state;

this.props.actions.login({
Copy link

@javier-pepe javier-pepe Jul 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if a variable has the same name as a key, it can be assigned in the following way instead

this.props.login({ username, password });

otherwise the state destructuring into { username, password } creating intermediate variables would be unnecessary since we could just do the following and avoid the intermediate variable creation. The shorthand version stated above should be preferred though.

({ username: this.state.username, password: this.state.password })

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

handleOnChange = (e) => {
const { name, value } = e.target;

this.setState({
Copy link

@javier-pepe javier-pepe Jul 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should add at least a couple of basic checks, at least if name is not falsy, and if the value is different than the prior state value for that name key, only then updat ethe state for that key with setState,
otherwise we endup updating unecesarily a key with the same exact value in the state,
and in the unlickely case the name is undefined or empty, we endup invoking setStat e with invalid keys or causing exceptions.

Perhaps the use of something along these lines would be more efficient

if (name && (this.state[name] !== value || !this.state[name])) {
    this.setState({
        [name]: value
    });
}


const mapStateToProps = state => {
return {
auth: state.auth

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could rewrite this using destructuring and shorthand object assignment

const mapStateToProps = ({ auth }) => ({ auth });

@@ -0,0 +1,27 @@
.card-container.card {
max-width: 350px;
padding: 40px 40px;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the outer padding is the same value (vertical == horizontal),
there would be no need to specify them separately.

padding: [vertical] [horizontal];

rather please use

padding: 40px;

background-color: #F7F7F7;
padding: 20px 25px 30px;
margin: 0 auto 25px;
margin-top: 50px;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to override margin-top separately, it could be defined directly like so

margin: [top] [horizontal] [bottom];

Please replace the following

margin: 0 auto 25px;
margin-top: 50px;

with the appropriate definition
margin: 50px auto 25px;


expect(keys(props.actions)).toEqual(expectedActions);
});
expect(wrapper.find('section')).toHaveLength(1);
Copy link

@javier-pepe javier-pepe Jul 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add missing tests to make this one and all other new tests valid and more robust,

Testing we have a section element is valid, but alongside all other components and elements we are expecting to have in the rendered component.
We should test against functionality rather than implementation.

otherwise testing we just have a section expect(wrapper.find('section')).toHaveLength(1);
is fragile since the section element could be completely empty and the unit test will still pass. but in fact the application will be broken to the user.

The more robust way to test here would be to check against the components we are expecting and the actual output rendered.

There are at least 5 required checks missing here.

let section;

beforeAll(() => {
    section = wrapper.find('section');
});

LoginNavigation

it('should have one <LoginNavigation>', () => {
    expect(section.find('LoginNavigation').length).toBe(1);
});

Container

it('should have one `div` with class `container`', () => {
    expect(section.find('div.container').length).toBe(1);
});

Card

it('should have one card `div` with class `card-container`', () => {
    expect(section.find('div.card.card-container').length).toBe(1);
});

Profile Image

it('should have one `img` with class `profile-img`', () => {
    // optionaly we could also check against the image url
    expect(section.find('img.profile-img').length).toBe(1);
});

LoginFormMain

it('should have one `<LoginFormMain>` ', () => {
    expect(section.find('LoginFormMain').length).toBe(1);
});

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put a comment in the initial PR when was created:
Tests and clean code are pending

I'm working on this, base PR.

@@ -0,0 +1,64 @@
import React from 'react';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not find the test for this component, does it have one?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@@ -0,0 +1,72 @@
import React from 'react';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could not find the test for this component, does it have one?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@@ -0,0 +1,55 @@

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could not find corresponding test

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@@ -0,0 +1,54 @@
import React from 'react';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could not find test

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

} = this.props;
let dataToRender = null;

if (this.props.showFooter) {
Copy link

@javier-pepe javier-pepe Jul 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could not find the added test case for this newly added if (this.props.showFooter) branch, this will lower our branches coverage.

We should make sure to test all newly intoduced modifications and functionality, and as a rule of thumb always bring our coverage up or at least keep it the same after modifications or additions.

We should add the corresponding test, thanks

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@javier-pepe javier-pepe added the Feature A new feature/functionality/improvement label Jul 19, 2018
@IvanScoles IvanScoles changed the base branch from 25-address-general-comments to develop July 23, 2018 00:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature A new feature/functionality/improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants