【问题标题】:Updating redux store in one component is not reflected in another component在一个组件中更新 redux 存储不会反映在另一个组件中
【发布时间】:2020-03-25 05:39:29
【问题描述】:

我有一个 YearFilter 组件:

import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { Dropdown } from 'semantic-ui-react';
import {
  changeYear
} from '../../actions/menuFiltersActions';
import makeDropdownOptions from '../../lib/makeDropdownOptions';

export class YearFilter extends React.Component {
  constructor(props) {
    super(props);
    this.handleChangeYear = this.handleChangeYear.bind(this)
  }

  handleChangeYear(year) {
    console.log(year)
    this.props.changeYear(year)
    this.props.getClientActuals()
    // this.props.testSelectedYear()

    console.log(this.props.years.selectedYear)
  }

  render() {
    return (
      <div id="YearFilter">
        <span>Year</span>
        <Dropdown
          // fluid
          selection
          loading={!this.props.years.data.length}
          options={makeDropdownOptions(this.props.years.data)}
          value={this.props.years.selectedYear}
          onChange={(e, { value }) => this.handleChangeYear(value)}
        />
      </div>
    );
  }
}

YearFilter.propTypes = {
  years: PropTypes.exact({
    data: PropTypes.array,
    selectedYear: PropTypes.number
  }).isRequired,
  changeYear: PropTypes.func.isRequired,
  getClientActuals: PropTypes.func.isRequired,

};

export default connect(
  store => ({
    years: store.menuFilters.years
  }),
  {
    changeYear
  }
)(YearFilter);

每当用户从下拉列表中选择年份时,我都会使用它来更新 Redux 商店中的 menuFilters.years.selectedYear 属性。

父组件如下:

import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { Header, Button, Icon, Loader} from 'semantic-ui-react';
import {
  changeYear
} from '../../actions/menuFiltersActions';
import {
  saveSalesHouseForecastSpendData
} from '../../actions/forecastSpendDataActions';
import './TabDataEntry.css';
import DataEntryFiltersSidebar from '../DataEntryFiltersSidebar/DataEntryFiltersSidebar';
import YearFilter from '../YearFilter/YearFilter';
import PivotTable from '../PivotTable/PivotTable';
import PivotTableAPI from '../../api/PivotTableAPI';
import processForecastSpendData from '../../lib/processForecastSpendData';
import { SALESHOUSE_LEVEL, MONTHS } from '../../constants';


export class TabDataEntry extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isSavingChanges: false,
      showNotification: false,
      showPivotTable: false
    }

    this.getClientActuals = this.getClientActuals.bind(this);
    this.getClientActualsFilters = this.getClientActualsFilters.bind(this);
    this.saveForecasts = this.saveForecasts.bind(this);
    this.collectChangedForecasts = this.collectChangedForecasts.bind(this);
    this.testSelectedYear = this.testSelectedYear.bind(this);
  }

  componentDidUpdate(prevProps) {
    if (prevProps.menuFilters.years.data !== this.props.menuFilters.years.data){
      this.props.changeYear(this.props.menuFilters.years.data[0]);
    }
  }

  getClientActualsFilters(extraParams) {
    const filters = {
      year: this.props.menuFilters.years.selectedYear,
      client_id: this.props.menuFilters.brands.selectedBrand,
      ...extraParams
    };
    // console.log(filters)

    if (this.props.menuFilters.media.selectedMedia) {
      filters.media_id = this.props.menuFilters.media.selectedMedia;
    }

    if (this.props.menuFilters.media.selectedBooking) {
      filters.booking_id = this.props.menuFilters.media.selectedBooking;
    }

    return filters;
  }

  testSelectedYear(){
    console.log(this.props.menuFilters.years.selectedYear)
  }

  async getClientActuals() {
    try {
      const clientActuals = await PivotTableAPI.getClientActuals(this.getClientActualsFilters());
      this.props.saveSalesHouseForecastSpendData(
        processForecastSpendData(clientActuals, { level: SALESHOUSE_LEVEL })
      );
      this.setState({showPivotTable: true})
    } catch (e) {console.log(e)}
  }


  async saveForecasts() {
    this.setState({ isSavingChanges: true })
    let forecast = this.collectChangedForecasts(this.props.forecastSpendData);

    try {
      const response = await PivotTableAPI.saveForecasts({
        media_id: this.props.menuFilters.media.selectedMedia,
        booking_id: this.props.menuFilters.media.selectedBooking,
        advertiser_id: this.props.menuFilters.brands.selectedBrand,
        year: this.props.menuFilters.years.selectedYear,
        forecast_period: [],
        forecast
      });

      this.notify(response ? 'positive' : 'negative');
    } catch (e) { console.log(e) }
    this.setState({ isSavingChanges: false })
  }


  collectChangedForecasts(data) {
    const childrenKeys = {
      Saleshouse: 'folios',
      Folio: 'suppliers'
    };

    let changedForecasts = Object.values(data).reduce((changedForecasts, saleshouse) => {
      for (let month in MONTHS) {
        // check if forecast has changed and if this element is the level at which data was entered
        if (saleshouse[MONTHS[month]]
        && saleshouse[MONTHS[month]].changed
        && saleshouse[MONTHS[month]].forecastLevel === saleshouse.level) {
          changedForecasts.push({
            month: parseInt(month) + 1,
            sales_house_id: saleshouse.SaleshouseId,
            folio_id: saleshouse.FolioId,
            supplier_id: saleshouse.SupplierId,
            forecast_level: saleshouse[MONTHS[month]].forecastLevel,
            metric: saleshouse[MONTHS[month]].forecast
          });
        }
      }

      let children = saleshouse[ childrenKeys[saleshouse.level] ]
      if (children && Object.values(children).length) {
        changedForecasts.push(...this.collectChangedForecasts(children))
      }

      return changedForecasts;
    }, [])

    return changedForecasts
  }

  notify(state) {
    this.setState({ showNotification: state })
    setTimeout(() => this.setState({ showNotification: false }), 5000)
  }


  render() {
    return (
        <div id="TabDataEntry">
          <DataEntryFiltersSidebar
            getClientActuals={this.getClientActuals}
          />


        <div id="main-container">
          <div className="header-section">
            <Header as="h2">Investment forecasts</Header>

            <p>
              Select a Client / Advertiser to view and confirm the current share deal spends.
              <Loader as="span" active={this.state.isSavingChanges} inline size="mini" inverted />
              { this.state.showNotification &&
                <span className={`notification ${this.state.showNotification}`}><Icon name="checkmark"/>Saved</span>
              }
            </p>
          </div>



          {this.state.showPivotTable && 
            <>
              <YearFilter
                getClientActuals={this.getClientActuals}
                testSelectedYear={this.testSelectedYear}
              />
              <div id="overflowing-container">
                  <PivotTable
                    getClientActualsFilters={this.getClientActualsFilters}
                  />

                  <div id="bottom-buttons">
                    <div>
                      <Button
                        primary
                        onClick={this.saveForecasts}
                        disabled={!Object.keys(this.props.forecastSpendData).length}
                      >
                        <Loader active={this.state.isSavingChanges} inline size="mini" inverted /> Save Changes
                      </Button>
                      <Button
                        basic
                        onClick={this.getClientActuals}
                        disabled={!Object.keys(this.props.forecastSpendData).length}
                      >
                        Cancel
                      </Button>
                    </div>
                    <Button basic disabled><Icon name='balance scale'/>Rebalance Calculator</Button>
                    <Button basic disabled><Icon name='file excel outline'/>Export as XLS</Button>
                  </div>
              </div>
            </>
          }
        </div>
      </div>
    );
  }
}

TabDataEntry.propTypes = {
  menuFilters: PropTypes.objectOf(PropTypes.object).isRequired,
  forecastSpendData: PropTypes.objectOf(PropTypes.object).isRequired,
  saveSalesHouseForecastSpendData: PropTypes.func.isRequired,
  changeYear: PropTypes.func.isRequired
};

export default connect(
  store => ({
    menuFilters: store.menuFilters,
    forecastSpendData: store.forecastSpendData
  }),
  { saveSalesHouseForecastSpendData, changeYear }
)(TabDataEntry);

我遇到的问题是,当 YearFilter 更新 Redux 存储中的 menuFilters.years.selectedYear 时,父组件(TabDataEntry)仍然检索先前设置的值。

我做错了什么?感谢任何帮助。

【问题讨论】:

    标签: javascript reactjs redux state


    【解决方案1】:

    您正在尝试访问

    this.props.changeYear(year)
    

    YearFilter组件中,但我没有看到它是从父组件传递过来的:

      <YearFilter
        getClientActuals={this.getClientActuals}
        testSelectedYear={this.testSelectedYear}
      />
    

    如果changeYear 是动作创建者,只需使用:

    changeYear(year);
    

    在组件YearFilter的方法handleChangeYear中。

    【讨论】:

      【解决方案2】:

      我意识到我的问题的原因是我假设 Redux 存储在我调用 changeYear 操作时立即更新。然而情况并非如此,Redux 存储不会立即更新。

      在 YearFilter 组件中,我立即调用 this.props.changeYear(year),然后调用 this.props.getClientActuals()。当 getClientActuals 被触发时,它仍在使用尚未更新的 Redux 存储,因此是我的问题。

      我意识到我需要在更新 Redux 存储时触发回调函数,以确保 getClientActuals() 与最新的 Redux 存储配合使用。

      使用this question的答案,我在 YearFilter 中采用了生命周期方法 componentDidUpdate() 并实现如下:

      if (this.props.years.selectedYear !== prevProps.years.selectedYear) {
        this.props.getClientActuals()
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-08-07
        • 2018-03-01
        • 2019-03-08
        • 2019-06-27
        • 2021-07-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多