0

My problem is that when the render reaches {this.displayUsage()}, inside the function displayUsage() the function uses the previous this.props.stoveUsage rather than the new one I get from the function getUsage().

This is how currently goes:

  1. Currently viewType state is 'day'
  2. I press a button from the segment, for example 'month'
  3. onPressSegment() runs, then setState(viewType), component re-renders, due to state change using old stoveUsage props, displayUsage() runs using old prop, getUsage() runs, prop is updated

PROBLEM: I need the component to render and use the NEW prop for displayUsage function, not the OLD one.

class Stats extends React.Component {
  state = {
    viewType: 'day',
    dt: moment(),
    device: {}
  }
  getUsage = () => {
    let dt = this.state.dt
    let vt = this.state.viewType
    let dev = this.state.device
    let date
    if (vt === 'day') {
      date = moment(dt).format('YYYY-MM-D')
    } else if (vt === 'week') {
      date = moment(dt).startOf('week').format('YYYY-MM-D')
    } else {
      date = moment(dt).format('YYYY-MM')
    }
    this.props.getStoveUsage(dev._id, vt, date)
  }    

  onPressSegment = (viewType) => {
    console.log('click on segment ', viewType)
    this.setState({ viewType }, () => {
      this.getUsage()
    })
  }
  displayUsage = () => {
    if (!this.props.fetching) {
      const total = this.props.stoveUsage.total
      const average = this.props.stoveUsage.average
      const usage = this.props.stoveUsage.usage
      if (this.state.viewType === 'day') {
        return <UsageDay totalUsage={total} average={average} usage={usage} date={this.state.dt} />
      } else if (this.state.viewType === 'week') {
        return <UsageWeek />
      } else {
        return <UsageMonth totalUsage={total} usage={usage} />
      }
    }
  }

render () {
   return (
        <Segment>
          <Button first active={this.state.viewType === 'day'} onPress={() => this.onPressSegment('day')}><Text>Daily</Text></Button>
          <Button active={this.state.viewType === 'week'} onPress={() => this.onPressSegment('week')}><Text>Weekly</Text></Button>
          <Button last active={this.state.viewType === 'month'} onPress={() => this.onPressSegment('month')}><Text>Monthy</Text></Button>
        </Segment>

        <Content contentContainerStyle={{ flex: 1 }}>
          {this.displayUsage()}
        </Content>
   }

const mapStateToProps = (state) => {
  return {
    fetching: state.smarturns.fetching,
    devices: state.smarturns.devices,
    selectedDeviceId: state.smarturns.selectedDeviceId,
    stoveUsage: state.smarturns.usage
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    selectDevice: (selectedDeviceId) => 
        dispatch(SmarturnsActions.deviceSelected(selectedDeviceId)),
    getStoveUsage: (deviceId, viewType, start) => 
        dispatch(SmarturnsActions.getUsage(deviceId, viewType, start))
  }
}

SmarturnsAction.getUsage code:

export function* getUsage (api, action) {
  const { deviceId, viewType, start } = action
  console.log('Enter getUsage. action=', action)
  try {
    const response = yield call(api.getUsage, deviceId, viewType, start)
    if (response.ok) {
      console.log('Enter getUsage. data=' + response.data)
      yield put(SmarturnsActions.usageFetched(response.data))
    } else {
      yield put(SmarturnsActions.apiFailure(response, action))
    }
  } catch (err) {
    yield put(SmarturnsActions.apiFailure(err, action))
  }
}

SmarturnsActions.usageFetched code:

// received stove usage
export const usageFetched = (state, {usage}) => state.merge({fetching: 
false, usage})
Lalaluye
  • 71
  • 1
  • 9
  • What happens in the SmarturnsActions.getUsage function? Could you show a reducer that manages the state for state.smarturns.usage? – slawekkolodziej Dec 31 '17 at 09:48
  • Yes, please see above. – Lalaluye Dec 31 '17 at 10:15
  • I think that you're not triggering a proper action in your mapDispatchToProps. Can you see a JS error like `Actions may not have an undefined "type" property` in the console? Also, I can't see that in your code, but I assume that somewhere below you call: `connect(mapStateToProps, mapDispatchToProps)(Stats)`, right? – slawekkolodziej Dec 31 '17 at 12:46
  • yes. I did call connect(). Eventually the props does update. but that is after the render. – Lalaluye Dec 31 '17 at 21:16
  • I must be missing something here? Isn't `getUsage` an async call to an API? Until that call returns, you only have the old value to render, but `fetching` should have been flipped so the output should be suppressed anyway until the new usage data is retrieved. – Michael Peyper Dec 31 '17 at 22:32
  • Also, there appears to be a missing piece to the puzzle as the parameters passed to `SmarturnsActions.getUsage` in `mapDisatchToProps` are different from `function* getUsage (api, action)` and the shown `SmarturnsAction.getUsage` is a saga, which as far as I'm aware, cannot be directly dispatched (it would be _listening_ for a trigger action). Presumably this missing action creator is where `fetching` get flipped to `true` as well? Is there more code to share? – Michael Peyper Dec 31 '17 at 22:34
  • Try to trigger a rerender with this.forceUpdate(). More [here](https://stackoverflow.com/questions/30626030/can-you-force-a-react-component-to-rerender-without-calling-setstate). – Sir hennihau Jan 02 '18 at 19:16

0 Answers0