Search code examples
reactjsreduxreact-reduxredux-form

Organizing React app in component/container


I am working with React Redux Starter Kit and am trying to organize my code in component/container and struggling a bit with finding the right process. I realize it's a bit if a loose line but Id like some input on how to better organize things.

Currently I have 3 files - CheckoutForm (a redux form), CheckoutView (a simple page that has the form), and CheckoutContainer which right now does nearly nothing. Following the advice from reading about component/container separation it seems that the CheckoutView should be the same but that I should perhaps pull the submit function out of it and put that in the CheckoutContainer? How would I best rewire this code:

CheckoutContainer.js

import Checkout from '../components/CheckoutView'

export default Checkout

CheckoutView.js

import React from 'react'
import classes from './CheckoutView.scss'
import CheckoutForm from '../forms/CheckoutForm'

const submit = require('../modules/submit').default

export const CheckoutView = () => (
  <div>
    <h4 className={classes.header}>Checkout</h4>
    <CheckoutForm onSubmit={submit}/>
  </div>
)

export default CheckoutView

CheckoutForm.js

import { Field, reduxForm } from 'redux-form'

const renderField = ({ input, label, type, meta: { touched, error } }) => (
  <div>
    <label>{label}</label>
    <div>
      <input {...input} placeholder={label} type={type}/>
      {touched && error && <span>{error}</span>}
    </div>
  </div>
)

const CheckoutForm = (props) => {
  const { error, handleSubmit, pristine, reset, submitting } = props
  return (
    <form onSubmit={handleSubmit}>
      <Field name="username" type="text" component={renderField} label="Username"/>
      <Field name="password" type="password" component={renderField} label="Password"/>
      {error && <strong>{error}</strong>}
      <div>
        <button type="submit" disabled={submitting}>Log In</button>
        <button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values</button>
      </div>
    </form>
  )
}

export default reduxForm({
  form: 'checkoutForm' 
})(CheckoutForm)

Solution

  • The main purpose of the container is to digest the information the component needs to know from the store and pass it to it, and also to provide callbacks as props that will handle the dispatching of actions in the container, so the component only has to pass the parameters it knows, and that's it. This way your component knows and does exactly what it needs, and nothing else, and it's not even aware of Redux at all.

    Looking at your code, you got it right

    I should perhaps pull the submit function out of it and put that in the CheckoutContainer

    That's exactly what you should do, as it is the only thing the component does it should not care about.

    In your container

    import { connect } from 'react-redux';
    import Checkout from '../components/CheckoutView';
    
    const submit = require('../modules/submit').default
    
    const mapStateToProps = state => {
        return { };
    };
    
    const mapDispatchToProps = dispatch => {
        return { onSubmit: submit };
    };
    
    export default connect(mapStateToProps, mapDispatchToProps)(Checkout);
    

    And in the View:

    import React from 'react'
    import classes from './CheckoutView.scss'
    import CheckoutForm from '../forms/CheckoutForm'
    
    export const CheckoutView = ({ onSubmit }) => (
      <div>
        <h4 className={classes.header}>Checkout</h4>
        <CheckoutForm onSubmit={onSubmit}/>
      </div>
    )
    
    export default CheckoutView
    

    As your application scales, you'll be ready to pass any information from the state to the component in mapStateToProps and additional callbacks in mapDispatchToProps.