1
votes

Im relatively new to React and Redux, and I created a simple ajax Email form for learning. The issue i'm having is that after form submission I set the store state back to Initialstate, which should clear all fields but it doesn't. I can see the store changes in redux logger, *see image attached but these changes are not showing on the ui. Is my store not mapping to state correctly? Or am I mutating state somewhere?

My reducer looks like the following:

export default function contactForm(state = initialState.formValues, action) {
  switch (action.type) {
    case types.FORM_RESET:
      return initialState.formValues;
    case types.FORM_SUBMIT_SUCCESS:
      return Object.assign({}, action.message);
    default:
      return state;
  }
}

Combine Reducers:

import { combineReducers } from 'redux';
import message from './formReducer';
import ajaxCallsInProgress from './ajaxStatusReducer';

const rootReducer = combineReducers({
  message,
  ajaxCallsInProgress
});

My initialstate looks like:

export default {
  formValues: {
    name: '', email: '', message: '',
  },
  ajaxCallsInProgress: 0,
};

My Actions Look like this:

export function messageSuccess(message) {
  return { type: types.FORM_SUBMIT_SUCCESS, message };
}

export function resetForm() {
  return { type: types.FORM_RESET };
}

export function saveMessage(message) {
  return function (dispatch) {
    dispatch(beginAjaxCall());
    return messageApi.saveMessage(message)
    .then(() => {
      dispatch(messageSuccess(message));
      dispatch(resetForm());
    }).catch((error) => {
      dispatch(ajaxCallError(error));
      throw (error);
    });
  }
}

In the view I am mapping state to props via:

constructor(props, context) {
  super(props, context);
  this.state = {
   message: Object.assign({}, this.props.message),
  }
}

render() {
  return (
    <ContactForm
      onChange={this.updateMessageState}
      onSubmit={this.submitForm}
      message={this.state.message}
    />
  );
}

function mapStateToProps(state) {
  return {
   message: state.message,
  };
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(formActions, dispatch)
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(ContactSection);

Log showing store changes

enter image description here

I would be very grateful to any advice.

2
Can you provide what your initial state looks like? I think the source of the issue is that you're mutating the state in your reducer.Tom
Tom, ive just added initialstateGraeme Paul

2 Answers

2
votes

I've updated my answer with the code that I think should work for your example. You were pretty close, however based on your comments on trying to combine two reducers, I've created two reducers so you can see how it works.

/* constants.js */
export default {
  FORM_RESET: 'FORM_RESET',
  FORM_SUBMIT: 'FORM_SUBMIT',
  AJAX_REQUEST: 'AJAX_REQUEST'
};


/* form-values-reducer.js */
const initialState = {
  name: '',
  email: '',
  message: ''
};

export default const formValuesReducer = (state = initialState, action) => {
  switch (action.type) {
    case Constants.FORM_SUBMIT:
      return {
        ...state,
        message: action.message
      };

    case Constants.FORM_RESET:
      return {
        ..state,
        name: '',
        email: '',
        message: ''
      };

    default:
      return state;
  }
};

/* ajax-request-reducer.js */
const initialState = {
  ajaxRequestCount: 0
};

export default const ajaxRequestReducer = (state = initialState, action) => {
  switch (action.type) {
    case Constants.AJAX_REQUEST:
      return {
        ...state,
        ajaxRequestCount: state.ajaxRequestCount + 1
      };
    default:
      return state;
  }
};

/* action-creators.js */
export const resettedForm = () => {
  return {
    type: Constants.FORM_RESET
  }
};

export const submittedForm = (message) => {
  return {
    type: Constants.FORM_SUBMIT,
    message
  }
};

export const ajaxRequested = () => {
  return {
    type: Constants.AJAX_REQUEST
  }
};

/* actions */
export const resetForm = (dispatch) => {
  return () => {
    dispatch(resettedForm());
  }
};

export const submitForm = (dispatch) => {
  return (message) => {
    dispatch(ajaxRequested());
    dispatch(submittedForm(message));
  }
};

/* reducers.js */
import { combineReducers } from 'redux';
import ajaxRequest from './ajax-request-reducer';
import formValues from './form-values-reducer';

export default combineReducers({
  ajaxRequest,
  formValues
});

/* Component */
import React from 'react';
import { connect } from 'react-redux';
import { resetForm, submitForm } from './actions';

const App = (props) => (
  <div>Your app UI stuff goes here</div>
);

const mapStateToProps = (state) => {
  return {
    name: state.formValues.name,
    email: state.formValues.email,
    message: state.formValues.message,
    ajaxRequestCount: state.ajaxRequest.ajaxRequestCount
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    resetForm: resetForm(dispatch),
    submitForm: submitForm(dispatch)
  }
};

export default connect(mapStateToProps, mapDispatchToProps)(App);

I've not run this through anything, so there may be some mistakes in the code here and there.

0
votes

I added the following which updated the state. I'm not sure if this is best practise with Redux, but it worked

componentWillReceiveProps(nextProps) {
    this.setState({ message: nextProps.mail });
  }