Action is not dispatched again after the props cha

2019-05-30 01:09发布

问题:

I have the following React Component:

import React from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {loadJobTitleSkills} from '../../actions/jobTitleSkillsActions';
import SkillList from './SkillList';

class SkillPage extends React.Component {

  componentDidMount() {
    if (this.props.user_positions[0]) {
      this.props.dispatch(loadJobTitleSkills(this.props.user_positions[0].job_title_id));
    }
  }

  render() {
    return (
      <div>
        <SkillList skills={this.props.job_title_skills} />
      </div>
    );
  }
}

SkillPage.propTypes = {
  job_title_skills: PropTypes.array.isRequired,
  user_positions: PropTypes.array.isRequired,
};

const mapStateToProps = state => {
  return {
    job_title_skills: state.job_title_skills,
    user_positions: state.user_positions
  };
};

export default connect(mapStateToProps)(SkillPage);

state.user_positions = {} when the component is rendered as a previous component is a form that submits state.user_positions. Right after the SkillPage component is rendered, state.user_positions is populated, which I validated in the Redux chrome extension.

The problem is this SkillPage component is not automatically re-rendering. I expect the component to automatically detect state.user_positions has changed and rerun componentDidMount, which would then dispatch loadJobTitleSkills().

What am I doing wrong here? Why isn't my component re-rendering whenever state.user_positions is modified?

userPositionReducer.js:

import * as types from '../actions/actionTypes';

const initialState = {}

export default function userPositionReducer(state = initialState, action) {
  switch (action.type) {
    case types.CREATE_USERPOSITION_SUCCESS:
      return action.user_position
    case types.LOAD_USERPOSITION_SUCCESS:
      return action.user_positions
    default:
      return state;
  }
}

jobTitleSkillsReducer.js:

import * as types from '../actions/actionTypes';

const initialState = []

export default function jobTitleSkillsReducer(state = initialState, action) {
  switch (action.type) {
    case types.LOAD_JOBTITLESKILLS_SUCCESS:
     return action.job_title_skills
    default:
      return state;
  }
}

Update

I was able to get it to "work" by adding the following to my SkillPage component and removing componentDidMount()

  componentDidUpdate() {
    if (this.props.user_positions[0] && this.props.job_title_skills.length === 0) {
      this.props.dispatch(loadJobTitleSkills(this.props.user_positions[0].job_title_id));
    }
  }

This feels super hacky. Is there a better way to do this?

Update 2

Working with this added:

  componentWillReceiveProps(nextProps) {
    if (nextProps.user_positions && nextProps.user_positions.length > 0) {
      if (this.props.user_positions[0] !== nextProps.user_positions[0]) {
        this.props.dispatch(loadJobTitleSkills(nextProps.user_positions[0].job_title_id));
      }
    }
  }

This solution still feels super hacky.

Update 3 - Adding Action Creators

jobTitleSkillsActions.js

import * as types from './actionTypes';
import JobTitleSkillsApi from '../api/JobTitleSkillsApi';

export function loadJobTitleSkillsSuccess(job_title_skills) {
  return {type: types.LOAD_JOBTITLESKILLS_SUCCESS, job_title_skills};
}

export function loadJobTitleSkills(job_title_id) {
  return function(dispatch) {
    return JobTitleSkillsApi.getAllJobTitleSkills(job_title_id).then(skills => {
      dispatch(loadJobTitleSkillsSuccess(skills));
    }).catch(error => {
      throw(error);
    });
  };
}

UserPositionActions.js

import * as types from './actionTypes';
import userPositionsApi from '../api/UserPositionsApi';

export function createUserPositionSuccess(user_position) {
  return {type: types.CREATE_USERPOSITION_SUCCESS, user_position};
}

export function loadUserPositionSuccess(user_positions) {
  return {type: types.LOAD_USERPOSITION_SUCCESS, user_positions};
}

export function loadUserPositions() {
  // console.log('actions: loadUserPosition')
  return function(dispatch) {
    return userPositionsApi.getAllUserPositions().then(user_positions => {
      dispatch(loadUserPositionSuccess(user_positions));
    }).catch(error => {
      throw(error);
    });
  };
}

export function createUserPosition(user_position) {
  // console.log('actions: createUserPosition')
  return function (dispatch) {
    return userPositionsApi.createUserPosition(user_position).then(responseUserPosition => {
      dispatch(createUserPositionSuccess(responseUserPosition));
      return responseUserPosition;
    }).catch(error => {
      throw(error);
    });
  };
}

回答1:

ComponentDidMount lifeCycle function is only executed once, at the time of component Mount. What you want is the componentWillReceiveProps function which too which gets executed on every prop change

Do this

componentDidMount() {
    if (this.props.user_positions[0] ) {
      this.props.dispatch(loadJobTitleSkills(this.props.user_positions[0].job_title_id));
    }
  }
componentWillReceiveProps(nextProps) {

     if (this.props.user_positions[0] !== nextProps.userPositions[0]) {
      this.props.dispatch(loadJobTitleSkills(this.props.user_positions[0].job_title_id));
    }
}