Reputation: 5631
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".
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.
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.
Upvotes: 1
Views: 852
Reputation: 3019
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() {
...
}
}
Upvotes: 2