mohunt
mohunt

Reputation: 33

React\Redux render() not being called after change in state

I am attempting to populate a dropdown list from the server in a React/Redux project. The dropdown list in defined as a component and embedded in a parent component.

Parent component:

import React from 'react';
import Form from 'components/Form';
import { reduxForm } from 'redux-form';
import { Input } from 'components';
import FirstFormList from '../FirstFormList/FirstFormList.js';
import { firstform } from 'redux/modules/firstForm';

class FirstForm extends Form {
  render() {
    const {
      fields: { dateTime }
    } = this.props;
    return (
      <form onSubmit={this.handleApiSubmit(firstform)} className="form-horizontal">
        {this.renderGlobalErrorList()}
        <Input field={dateTime} label="Time Stamp" />
        <div className="form-group">
          <div className="col-md-1" />
          <label htmlFor="select1" className="col-md-1">Select Option</label>
          <div className="col-md-10">
            <FirstFormList />
          </div>
        </div>
        <div className="form-group">
          <div className="col-md-offset-2 col-md-10">
            <button type="submit" className="btn btn-default">Submit</button>
          </div>
        </div>
      </form>
    );
  }
}

FirstForm = reduxForm({
  form: 'firstform',
  fields: ['dateTime']
}
)(FirstForm);

export default FirstForm;

The child component FirstFormList

import React from 'react';
import { connect } from 'react-redux';
import { fetchfirstformlist } from 'redux/modules/firstFormList';

class FirstFormList extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      items: []
    };
  }

  componentDidMount() {
    this.props.fetchfirstformlist();
  }

  render() {
    let optionItems = [];
    if (this.props.items !== undefined && this.props.items !== null) {
      const items = this.props.items;
      optionItems = items.map((item) =>
        <option key={item.value}>{item.label}</option>
      );
    }
    return (
      <div>
        <select className="form-control">
          {optionItems}
        </select>
      </div>
    );
  }
}

export default connect(
  state => ({ items: state.items }),
  { fetchfirstformlist })(FirstFormList);

The child component reducer:

export const FIRSTFORMLISTFETCH_START = 'react/firstform/FIRSTFORMGETLIST_START';
export const FIRSTFORMLISTFETCH_COMPLETE = 'react/firstform/FIRSTFORMGETLIST_COMPLETE';
export const FIRSTFORMLISTFETCH_ERROR = 'react/firstform/FIRSTFORMGETLIST_ERROR';

const initialState = {
};

export default function reducer(state = initialState, action = {}) {
  switch (action.type) {
    case FIRSTFORMLISTFETCH_COMPLETE:
      return {
        ...state,
        items: action.result.items
      };
    default:
      return state;
  }
}

export function fetchfirstformlist() {
  return {
    types: [FIRSTFORMLISTFETCH_START, FIRSTFORMLISTFETCH_COMPLETE, FIRSTFORMLISTFETCH_ERROR],
    promise: (client) => client.post('/api/firstform/getfirstformlist')
  };
}

After the componentDidMount fires can see the items returned by the server in the state if I set a break point in the FirstFormList compoenent:

export default connect(
  state => ({ items: state.items }),
  { fetchfirstformlist })(FirstFormList);

However the render() of the component does not fire after the items are attached to the state.

Upvotes: 0

Views: 1081

Answers (2)

mohunt
mohunt

Reputation: 33

I found the problem. The list items are stored in the state in state.firstFormList.items not state.items. The export should be:

export default connect(
  state => ({ items: state.firstFormList.items }),
  { fetchfirstformlist })(FirstFormList);

not

 export default connect(
      state => ({ items: state.items }),
      { fetchfirstformlist })(FirstFormList);

Upvotes: 0

devssh
devssh

Reputation: 1186

Ok, the state in export default connect(state => ({ items: state.items }),{ fetchfirstformlist})(FirstFormList);

is the reducer state, it is not React state. So you can remove this.state = {items: []}; as you are not using this.state.items in your render. You are getting the this.props.items from the connect state.items.

Secondly, are you checking the logs to see if there are errors in the reducers. (I guess if action.result is undefined it will throw an error on action.result.items). So can you check that action.result is not undefined or assign it a default value before you try to use action.result.items? You should also try to comment out lines like the one with the promise and see if it works. The line with the multiple types also looks suspicious. Also, you can try adding curly braces to your one liner anonymous methods and add additional logging and explicly return values.

Also, the way react lifecycles work is that the constructor is called only the first time a component renders and then the componentDidMount is also called only the first time the component renders.

Thirdly, why don't you debug the state of redux using the redux plugin or by {console.log("test", this.props)} in your render method and in your reducer. This way you will definitely find out where the error is since the information provided may not be sufficient.

If these 3 steps don't fix your answer or help you resolve it leave a comment and I'll help you.

Upvotes: 1

Related Questions