1

I have the following component which I use for navigation:

import React, { Component } from "react";
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';

class TabBar extends Component {
  constructor(props) {
    super(props);

    const noTankAvailable = this.props.tank.tankData.noAssignedTank;
    console.log("noTankAvailable", noTankAvailable);

    if (noTankAvailable === true || noTankAvailable === undefined) {
      this.tabs = [
        { label: "Registration", icon: faSimCard, url: "registration" }
      ];
    } else {
      this.tabs = [
        { label: "Status", icon: faChartBar, url: "status" },
        { label: "History", icon: faHistory, url: "history" },
        { label: "Properties", icon: faSlidersH, url: "properties" }
      ];
    }
    ...
  }
  ...
  render() {
    const { location, match } = this.props;
    const { pathname } = location;
    return (
      <div>
        <Tabs
          className="tabBar"
          contentContainerStyle={tabBarStyles.content}
          inkBarStyle={tabBarStyles.ink}
          tabItemContainerStyle={tabBarStyles.tabs}
          value={pathname}
        >
          {this.renderTabs(match)}
        </Tabs>
      </div>
    );
  }
}

const mapStateToProps = state => ({
  ...state
});

export default connect(mapStateToProps)(TabBar);

This is my redux reducer:

import {
  TANK_REQUEST,
  TANK_FAILURE,
  TANK_SUCCESS,
} from '../actions/tankActions';

const testState = {
  isLoading: false,
  currentTank: "",
  tankData: {}
};

export default (state = testState, action) => {
  switch (action.type) {
    case TANK_REQUEST:
      return Object.assign({}, state, { isLoading: true });
    case TANK_SUCCESS:
      if (action.tankData.length > 0) {
        const currentTank = action.tankData[0];
        const tankData = Object.assign({}, state.tankData, { [currentTank._id]: currentTank, isLoading: false });
        return Object.assign({}, state, { currentTank: currentTank._id, tankData });
      } else {
        const tankData = Object.assign({}, state.tankData, { noAssignedTank: true });
        return Object.assign({}, state, { tankData });
      }
    case TANK_FAILURE:
      return Object.assign({}, state, { currentTank: action.id, isLoading: false, error: action.err });
    default:
      return state
  }
}

The following scenario is given: When a user logs in, it fetches an API to get (water) tanks. If the user does not have an assigned tank, the application should redirect to the registration view and the navigation should only show "registration".

enter image description here So I fetch via an action. In my reducer I check if I got data and if not I will add noAssignedTank: true to my state. I want to check now in my TabBar component if this is true or not and hide/show navigation links depending on that.

My problem is that I would need to wait till the TANK_FETCHING_SUCCESS reducer is resolved to check if noAssignedTank is true.

enter image description here

You can see that the first console output is my console.log("noTankAvailable", noTankAvailable);. So my if/else statement is not working because at first it is undefined before it gets an value.

mrks
  • 4,642
  • 7
  • 38
  • 56

1 Answers1

2

You have to make this.tabs a state of your component and update it during lifecycle methods of your component.

Retrieving of tankData has been secured by additionnal tests (props.tank && props.tank.tankData).

Initial state is initialized in constructor with the props.

A reference on previous tank is also kept in state (prevTanData) for comparison when props will change (when the asynchronous value in store will be updated, the connected component will be notified by redux and a call to getDerivedStateFromProps will follow).

If prevTankData is the same as nextProps.tank.tankData then we return null to tell React the state does not need to change.

Note that for version React < 16, you wil have to use the instance method componentWillReceiveProps instead of the static getDerivedStateFromProps.

    class TabBar extends Component {
        constructor(props) {
            super(props);
            this.state = {
               tabs: TabBar.computeTabsFromProps(props),
               prevTankData: props.tank && props.tank.tankData,
            };
        };

        static computeTabsFromProps(props) {
            const noTankAvailable = props.tank &&
                                    props.tank.tankData &&
                                    props.tank.tankData.noAssignedTank;
            console.log("noTankAvailable", noTankAvailable);

            if (noTankAvailable === true || noTankAvailable === undefined) {
                return [
                    {
                        label: "Registration",
                        icon: faSimCard,
                        url: "registration"
                    }
                ];
            } else {
                return [
                    { label: "Status", icon: faChartBar, url: "status" },
                    { label: "History", icon: faHistory, url: "history" },
                    { label: "Properties", icon: faSlidersH, url: "properties" }
                ];
            }
        }
        static getDerivedStateFromProps(nextProps, prevState) {
            if ((nextProps.tank && nextProps.tank.tankData) !== prevState.prevTankData) {
                return {
                    prevTankData: nextProps.tank && nextProps.tank.tankData,
                    tabs: TabBar.computeTabsFromProps(nextProps),
                }
            }
            return null;
        }
        render() {
            ...
        }
    }
remix23
  • 2,261
  • 1
  • 8
  • 18
  • Thank you very much for your answer and solution! I really appreciate your work and that you described it very detailed so I can follow it and understand it. The only problem is that I get an error for `tabs: TabBar.computeTabsFromProps(props)` in `getDerivedStateFromProps ` that he can not find `props` defined. I update my question with your code! – mrks Mar 11 '19 at 14:03
  • 1
    That's a typo it should be `nextProps` instead of `props`. I've updated the answer. – remix23 Mar 11 '19 at 14:07