Why is the Redux store being updated with a duplicate nested key?

Viewed 1270

I'm starting with React and Redux, so I've connected a component to Redux store, I got this component which is supposed to be a very simple login:

import React, { Component } from 'react';
import { findDOMNode } from "react-dom";
import RaisedButton from 'material-ui/lib/raised-button';
import TextField from 'material-ui/lib/text-field';
import { connect } from 'react-redux';
import { loginRequest, loginSuccess, loginFailure } from '../actions';
import { browserHistory } from 'react-router';

class LoginForm extends Component {

  componentDidMount() {
    console.log(console.log(JSON.stringify(this.props,null,2)));
  }

  readyToSubmit() {

  }

  onSubmit(e) {
      e && e.preventDefault();
      let emailInput = findDOMNode(this.refs.emailInput).querySelector("input").value;
      let passwordInput = findDOMNode(this.refs.passwordInput).querySelector("input").value;
      if (emailInput.trim().length && passwordInput.trim().length) {
        if (emailInput.startsWith('hello')) {
          this.props.loginSuccess(emailInput);
        } else {
          console.log(emailInput);
          this.props.loginFailure(emailInput);
        }
      }
  }

  render() {
      let disabled = !!this.props.login.waiting || !!this.props.ready;
      return (
          <form onSubmit={::this.onSubmit}>
          <div>
            <TextField hint="E-mail" floatingLabelText="E-mail" ref="emailInput" onChange={::this.readyToSubmit}></TextField><br/>
          </div>
          <div>
            <TextField hint="Contraseña" floatingLabelText="Contraseña" type="password" ref="passwordInput" onChange={::this.readyToSubmit}></TextField><br/><br/>
          </div>
          <div>
            <RaisedButton type="submit" label="Acceder" primary={true} disabled={disabled} onClick={::this.onSubmit}/>
          </div>
        </form>
      );
  }
}

export default connect(
  state => {
    return {
      login: state.login
    }
  },
  {
    loginSuccess: loginSuccess,
    loginFailure: loginFailure,
    loginRequest: loginRequest
  })(LoginForm);

These are the actions:

export const loginSuccess = (email) => {
  return {type: 'LOGIN_SUCCESS', email: email };
}

export const loginFailure = (email) => {
  return {type: 'LOGIN_FAILURE', email: email };
}

export const loginRequest = (email) => {
  return {type: 'LOGIN_REQUEST', email: email};
}

These are my reducers:

const login = (state = {}, action) => {
  switch (action.type) {
    case 'LOGIN_REQUEST':
      return {
        ...state.login,
        login: {
          waiting: true,
          email: action.email
        }
      };
      break;
    case 'LOGIN_SUCCESS':
      return {
          ...state.login,
          login: {
            waiting: false,
            email: action.email,
          },
          auth: {
            token: state.token
          }
      };
      break;
      case 'LOGIN_FAILURE':
        return {
            ...state.login,
            login: {
              waiting: false,
              email: action.email,
              errors: action.errors
            }
          };
        break;
    default:
      return {
        ...state.login,
        login: {
          ready: false
        }
      };
  }
}
export default login

So, the first strange stuff I notice is that componentDidMount logs:

{
  "login": {
    "login": {
      "ready": false
    }
  }
}

So I need some help on:

  • I can't figure out why is nested thay way (login.login).
  • I'd like to get the button disabled only when both password and email fields are filled.
  • I understand that Redux would avoid me of working with React's state, and that the connect function ties some store properties ot the component properties and some dispatchers to properties too .. but how can I easily access fields, button's etc values as part of the props.
  • How can I ensure real deep merge at my reducers?
2 Answers
Related