Search code examples
javascriptreactjsreduxredux-thunk

Redux does not update the view after the reducer


SOLVED: defaultValue in fields form is not what I thought it was.

I don't know what I'm doing [chemistry dog picture here].

My component is called loadForm and loads data from an API call through a thunk action:

class loadForm extends Component {
  constructor(props) {
   super(props);
  }
  /*  Loads data to the form */
  componentDidMount() {
    let action = getAppoThunk(this.props.routeParams.id);
    this.props.dispatch(action);
    console.log('DidMount  appoArray>>'+ JSON.stringify(this.props.appo));
 }

componentWillReceiveProps(nextProps) {
  if (Object.keys(nextProps.appoArrayProp).length  !=  Object.keys(this.props.appoArrayProp).length ) {
  console.log('WWWW  NOT THE SAME nextProps  >>'+ JSON.stringify(nextProps.appo));
  console.log('WWWW  NOT THE SAME thisProps  >>' + JSON.stringify(this.props.appo));
  let action = getAppoThunk(this.props.routeParams.id);
  this.props.dispatch(action);  // thunk middleware dispatch
} else {
  console.log('ARE THE SAME this.props.appo>>' + JSON.stringify(this.props.appo));
}

}

the render:

   return(
     <form onSubmit={this.handleSubmit}>
      <label htmlFor="for_owner">Eingentümer (owner):</label>
      <input name="owner" defaultValue={this.props.appo.owner} />
    </form>
  );

this.props.appo is actually updated but the view doesn't change:

enter image description here

UPDATE

Thanks a lot guys for your help, since the beginning my reducer was:

    case RECEIVE_ONE_APPO
        Object.assign({}, state, {
         appArrayPop: action.appArrayPop
      });

In order to reduce complexity, I removed the array option replacing it with a simple string and, also, I deleted all the other reducers so my reducer now is just:

  const initialState = {
      eigentumer:  'initial'  // owner 
   };

  const appo_rdcer = (state = initialState, action) => { 
     switch (action.type){
      case RECEIVE_ONE_APPO:
         return Object.assign({}, state, {
            eigentumer: action.eigentumer
          });

Then I notice something weird:

return (
    <div id="responsive" className="modal hide fade" tabIndex="-1" >
      <Modal aria-labelledby='modal-label'
        style={modalStyle}
        backdropStyle={backdropStyle}
        show={this.state.showModal}
      >
      <Modal.Header>
         <Modal.Title>Modal Überschrift 1 --> {this.props.eigentumer}  </Modal.Title>
      </Modal.Header>
        <Modal.Body>
          <form onSubmit={this.handleSubmit}>
            <label htmlFor="for_owner">Eigentümer: 2 --> {this.props.eigentumer} </label>
            <input className="form-control" id="for_owner" name="owner" defaultValue={this.props.eigentumer} />
        </form>
      </Modal.Body>
      <Modal.Footer>
         <Button onClick={() => browserHistory.push('/appointments')}>Close</Button>
         <Button bsStyle="primary">Änderungen speichern</Button>
      </Modal.Footer>
    </Modal>
  </div>
  );

Result:

enter image description here

So, {this.props.eigentumer} is updated in places 1 and 2 but not in defaultValue={this.props.eigentumer} and that is why the view is not updated. Now I need to read this to know why this is happening.

Thanks a lot for your help.


Solution

  • How do you change appoArrayProp? If you are working on the same object, and just removing or adding keys, nextProps and this.props are pointing to the same object, and these will in turn always be equal. Yes, it's not intuitive, but that's how it goes.

    Let's say you have the object appoArrayProp:

    appoArrayProp = {
      "foo": 1,
      "bar": 2
    }
    

    If you change it by simply adding or removing a property, nextProps.appoArrayProp and this.props.appoArrayProp will both point to the same object:

    appoArrayProp = {
      "foo": 1,
      "bar": 2,
      "newProp": 3
    }
    

    and the Object.keys().length comparison will always return true. The solution is to create a new object, copying the properties of the current appoArrayProp, add or remove keys from the new object, then pass that on as a property:

    appoArrayProp = Object.assign({}, appoArrayProp)
    /* Do changes to appoArrayProp and render component */