componentWillReceiveProps / willUpdate不会触发特定的prop更改(mapStateToProps)

时间:2018-02-17 15:24:59

标签: reactjs redux

奇怪的问题。基本上,我的生命周期事件不会响应特定的道具变化而触发。这是一个React / Redux应用程序,这里是粗略的层次结构:

组件:
仪表板
--export
--Analytics
---- CreateGraphsDialog

减速:
DashboardReducer
ExportReducer
AnalyticsReducer
索引

操作:
仪表板
出口
分析

使用仪表板,导出和分析组件中的选择字段更改有问题的道具。它使用Dashboard操作和DashboardReducer更新,并通过mapStateToProps访问。当我尝试从主仪表板页面触发生命周期事件(例如componentWillReceiveProps或componentWillUpdate)时,它可以完美地工作。但是尝试从任一子页面“导出”或“分析”访问它们都会失败。道具更新,但其更新不会触发事件。

注意,有问题的道具/物品是一个字符串,所以我不认为这是一个不变性问题......除非其他减速器的不变性可能导致问题。还值得注意的是,组件更新以响应这些prop更改,并且prop已更改......它只是未触发的生命周期事件。

以下是一些代码:

仪表板操作

import axios from 'axios';
const qs = require('querystring-browser');

export const GOT_INVESTIGATIONS = 'got_investigations';
export const INVESTIGATIONS_ERROR = 'investigations_error';
export const SAVED_INVESTIGATION = 'saved_investigation';

export function getInvestigationsAction() {
  return async (dispatch) => {
    try {
      let jwtlocal = localStorage.getItem('user');

      let uri;
      if (process.env.NODE_ENV === 'production') {
        uri = window.location.protocol + '//' + window.location.host + '/jwtInvestigations'
      } else {
        uri = 'https://test.teamscope.co/jwtInvestigations'
      }

      const res = await axios.get(`${uri}`, {headers: {'TS-JWT': jwtlocal}});

      dispatch({type: GOT_INVESTIGATIONS, payload: res.data.investigations})
    } catch (error) {
      console.error(error)
      dispatch({
        type: INVESTIGATIONS_ERROR,
        payload: 'Error retrieving investigations. Please try again, or contact an administrator.'
      })
    }
  }
}

export function saveInvestigationAction(invest) {
  return async (dispatch) => {
    try {
      console.log("InvestInSave:", invest)
      dispatch({type: SAVED_INVESTIGATION, payload: invest})
    } catch (error) {
      console.error("SAVED_INVESTIGATION ERROR")
      console.error(error)
    }
  }
}

DashboardReducer:

import { GOT_INVESTIGATIONS, INVESTIGATIONS_ERROR, SAVED_INVESTIGATION } from '../actions/dashboard';

const initialState = {

};

export default function(state=initialState, action) {
  switch(action.type) {
    case GOT_INVESTIGATIONS:
      return { ...state, investsData: action.payload, investsError: undefined };
    case INVESTIGATIONS_ERROR:
      return { ...state, investsError: action.payload };
    case SAVED_INVESTIGATION:
      return { ...state, savedInvest: action.payload };
  }
  return state;
}

分析组件:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Button from 'material-ui/Button';
import TextField from 'material-ui/TextField';
import Table, { TableBody, TableCell, TableHead, TableRow } from 'material-ui/Table';
import Paper from 'material-ui/Paper';
import CreateGraphsDialog from './CreateGraphsDialog';

import Cached from 'material-ui-icons/Cached';

import { withRouter } from 'react-router-dom';
import { withStyles } from 'material-ui/styles';
import { connect } from 'react-redux';

import { getSavedGraphsAction, deleteGraphsAction, getGraphAction } from '../../actions/analytics';

import compose from 'recompose/compose';

const Highcharts = require('highcharts');
require('highcharts/modules/exporting')(Highcharts);
const ReactHighcharts = require('react-highcharts');

class Analytics extends Component {

    constructor(props) {
        super(props);
        this.props.getSavedGraphsAction(this.props.investigation);
    }

    componentWillUpdate(nextProps) {
        console.log(this.props.investigation)
        console.log(nextProps.investigation)
        //These consoles only fire if a *different* prop is changed, and even then they only ever show the same value for each.
        if (this.props.investigation && nextProps.investigation) {
            if (this.props.investigation !== nextProps.investigation) {
                console.log("!")
                //This console never fires.
                this.props.getSavedGraphsAction(nextProps.investigation)
            }
        }
    }

    refreshGraphs = () => {
        this.props.graphData.forEach((graph) => {
            this.props.getGraphAction(graph.values);
        });
    };


    render() {
        let graphs = null;
        if (this.props.graphData.length > 0) {
            console.log("graphs:", this.props.graphData)
            this.props.graphData.forEach((graph, i) => {
                if (graph.values.chartType !== 'basic') {
                    graph.config.exporting = {
                        buttons: {
                          deleteButton: {
                            enabled: true,
                            text: "Delete",
                            onclick: () => { this.props.deleteGraphsAction(graph.values.identifier) }
                          }
                        }
                      }
                }
            })
            graphs = (
                <div>
                {this.props.graphData.map((graph) =>
                    { return graph.values.chartType === 'basic' ?    
                        <div className="graphs"> 
                          <Table className={this.props.classes.table}>
                            <TableHead>
                              <TableRow>
                                <TableCell>Survey Name</TableCell>
                                <TableCell numeric>Minimum</TableCell>
                                <TableCell numeric>Maximum</TableCell>
                                <TableCell numeric>Mean</TableCell>
                                <TableCell numeric>Standard Deviation</TableCell>
                                <TableCell numeric>Variance</TableCell>
                                <TableCell numeric>Count</TableCell>
                                <TableCell><Button primary onClick={() => this.props.deleteGraphsAction(graph.values.identifier)}>Delete</Button></TableCell>
                              </TableRow>
                            </TableHead>
                            <TableBody>
                              <TableRow>
                                <TableCell>{graph.config.surveyTitle}</TableCell>
                                <TableCell numeric>{graph.config.stats.min}</TableCell>
                                <TableCell numeric>{graph.config.stats.max}</TableCell>
                                <TableCell numeric>{graph.config.stats.mean}</TableCell>
                                <TableCell numeric>{graph.config.stats.stdDev}</TableCell>
                                <TableCell numeric>{graph.config.stats.var}</TableCell>
                                <TableCell numeric>{graph.config.stats.count}</TableCell>
                              </TableRow>
                            </TableBody>
                          </Table>
                        </div>
                        : <div className="graphs">
                            <ReactHighcharts config={graph.config}/>
                          </div> }
                )}
                </div>
            )
        } else if (!this.props.investigation) {
            graphs = "Please select an investigation from the top right selection menu.";
        } else {
            graphs = null;
        }
            return (
                <div>
                    <CreateGraphsDialog/>
                    <Button className={this.props.classes.refresh} onClick={this.refreshGraphs}>
                        Refresh <Cached/>
                    </Button>
                    {graphs}
                </div>
            );
        }
}


const styles = {
 margin: 15,
  table: {
    minWidth: 700,
  },
  refresh: {
  },
  graphs: {
  }
};

function mapStateToProps(state, ownProps) {
  return { 
    investigation: state.dashboard.savedInvest,
    graphData: state.analytics.graphData,
    graphError: state.analytics.graphError
  };
}


export default compose(
  withRouter,
  connect(mapStateToProps, {getSavedGraphsAction, deleteGraphsAction, getGraphAction}),
  withStyles(styles)
)(Analytics);

-------------- UPDATE ---------------

我刚尝试了一个全新的组件,没有添加所有额外的东西。这是一个子组件,就像Analytics一样。它有同样的问题。这是代码:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

import { withRouter } from 'react-router-dom';
import { withStyles } from 'material-ui/styles';
import { connect } from 'react-redux';

import { getAuditsAction } from '../../actions/auditTrail';

import PropTypes from 'prop-types';

import compose from 'recompose/compose';

class AuditTrail extends Component {
    constructor(props){
        super(props);
        this.props.getAuditsAction(this.props.investigation)
    }

    componentWillUpdate(nextProps) {
        //These don't get called at all, since there aren't any other props to muddy the waters.
        console.log(this.props.investigation);
        console.log(nextProps.investigation);
    }

    render() {

        let notification;

        if (!this.props.investigation) {
            notification = "Please select an investigation from the top right selection menu.";
        } else if (this.props.auditError) {
            notification = this.props.auditError;
        }

        return (
            <div>
                {notification}
            </div>
        )
    }
}

const styles = {
    formContainer: {
        width: '30%'
    }
};

function mapStateToProps(state, ownProps) {
  return { 
    investigation: state.dashboard.savedInvest,
    audits: state.auditTrail.auditData,
    auditError: state.auditTrail.auditError
  };
}

export default compose(
  withRouter,
  connect(mapStateToProps, { getAuditsAction }),
  withStyles(styles)
)(AuditTrail);

1 个答案:

答案 0 :(得分:1)

来自提供的代码段

DashboardReducer:

case GOT_INVESTIGATIONS : return {
  ...state,
  investsData: action.payload,
  investsError: undefined
};

你只做一级浅版。相反,你应该这样做   像这样的状态更新。

return Object.assign({}, JSON.parse(JSON.stringify(state), {
  investsData: action.payload,
  investsError: undefined
}));

请参阅官方文档Redux#ImmutableUpdatePatterns